@effectify/prisma 0.1.1

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 (67) hide show
  1. package/README.md +154 -0
  2. package/dist/src/cli.d.ts +2 -0
  3. package/dist/src/cli.js +17 -0
  4. package/dist/src/commands/generate-effect.d.ts +2 -0
  5. package/dist/src/commands/generate-effect.js +73 -0
  6. package/dist/src/commands/generate-sql-schema.d.ts +2 -0
  7. package/dist/src/commands/generate-sql-schema.js +72 -0
  8. package/dist/src/commands/init.d.ts +4 -0
  9. package/dist/src/commands/init.js +102 -0
  10. package/dist/src/effect-prisma.d.ts +2 -0
  11. package/dist/src/effect-prisma.js +1771 -0
  12. package/dist/src/generators/prisma-effect-generator.d.ts +1 -0
  13. package/dist/src/generators/prisma-effect-generator.js +446 -0
  14. package/dist/src/generators/sql-schema-generator.d.ts +1 -0
  15. package/dist/src/generators/sql-schema-generator.js +58 -0
  16. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  17. package/package.json +53 -0
  18. package/prisma/dev.db +0 -0
  19. package/prisma/generated/client.d.ts +1 -0
  20. package/prisma/generated/client.js +5 -0
  21. package/prisma/generated/default.d.ts +1 -0
  22. package/prisma/generated/default.js +5 -0
  23. package/prisma/generated/edge.d.ts +1 -0
  24. package/prisma/generated/edge.js +141 -0
  25. package/prisma/generated/effect/index.ts +397 -0
  26. package/prisma/generated/effect/prisma-repository.ts +954 -0
  27. package/prisma/generated/effect/prisma-schema.ts +94 -0
  28. package/prisma/generated/effect/schemas/enums.ts +6 -0
  29. package/prisma/generated/effect/schemas/index.ts +2 -0
  30. package/prisma/generated/effect/schemas/types.ts +40 -0
  31. package/prisma/generated/index-browser.js +172 -0
  32. package/prisma/generated/index.d.ts +2360 -0
  33. package/prisma/generated/index.js +141 -0
  34. package/prisma/generated/package.json +144 -0
  35. package/prisma/generated/query_compiler_bg.js +2 -0
  36. package/prisma/generated/query_compiler_bg.wasm +0 -0
  37. package/prisma/generated/query_compiler_bg.wasm-base64.js +2 -0
  38. package/prisma/generated/runtime/client.d.ts +3180 -0
  39. package/prisma/generated/runtime/client.js +86 -0
  40. package/prisma/generated/runtime/index-browser.d.ts +87 -0
  41. package/prisma/generated/runtime/index-browser.js +6 -0
  42. package/prisma/generated/runtime/wasm-compiler-edge.js +76 -0
  43. package/prisma/generated/schema.prisma +31 -0
  44. package/prisma/generated/wasm-edge-light-loader.mjs +5 -0
  45. package/prisma/generated/wasm-worker-loader.mjs +5 -0
  46. package/prisma/migrations/20250721164420_init/migration.sql +9 -0
  47. package/prisma/migrations/20250721191716_dumb/migration.sql +49 -0
  48. package/prisma/migrations/migration_lock.toml +3 -0
  49. package/prisma/schema.prisma +31 -0
  50. package/prisma.config.ts +8 -0
  51. package/project.json +48 -0
  52. package/scripts/cleanup-tests.ts +26 -0
  53. package/scripts/generate-test-files.ts +93 -0
  54. package/setup-tests.ts +10 -0
  55. package/src/cli.tsx +23 -0
  56. package/src/commands/generate-effect.ts +109 -0
  57. package/src/commands/generate-sql-schema.ts +109 -0
  58. package/src/commands/init.ts +155 -0
  59. package/src/effect-prisma.ts +1826 -0
  60. package/src/generators/prisma-effect-generator.ts +496 -0
  61. package/src/generators/sql-schema-generator.ts +75 -0
  62. package/test/prisma-model.test.ts +340 -0
  63. package/test/utils.ts +10 -0
  64. package/tsconfig.json +20 -0
  65. package/tsconfig.lib.json +24 -0
  66. package/tsconfig.spec.json +15 -0
  67. package/vitest.config.ts +23 -0
@@ -0,0 +1,340 @@
1
+ import { layer } from '@effect/vitest'
2
+ import { Prisma, PrismaClient, TodoModel } from '@prisma/effect'
3
+ import * as PrismaRepository from '@prisma/effect/prisma-repository.js'
4
+ import { Effect, Layer, Option } from 'effect'
5
+ import { beforeEach, expect } from 'vitest'
6
+ import { prisma as db } from './utils.js'
7
+
8
+ // Create the Prisma layer manually to use the existing client instance
9
+ const ClientLayer = Layer.succeed(PrismaClient, { tx: db, client: db })
10
+ const PrismaLayer = Layer.merge(ClientLayer, Prisma.Default.pipe(Layer.provide(ClientLayer)))
11
+
12
+ layer(PrismaLayer)('Prisma Model Repository', (it) => {
13
+ beforeEach(async () => {
14
+ await db.todo.deleteMany({})
15
+ })
16
+
17
+ it.effect('create', () =>
18
+ Effect.gen(function* () {
19
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
20
+
21
+ const created = yield* todoRepo.create({
22
+ data: {
23
+ id: 1,
24
+ title: 'Create Test',
25
+ content: 'Content',
26
+ published: false,
27
+ authorId: 1,
28
+ },
29
+ })
30
+ expect(created.id).toBe(1)
31
+ expect(created.title).toBe('Create Test')
32
+ }),
33
+ )
34
+
35
+ it.effect('createMany', () =>
36
+ Effect.gen(function* () {
37
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
38
+
39
+ const result = yield* todoRepo.createMany({
40
+ data: [
41
+ { id: 1, title: 'Todo 1', content: 'C1', published: false, authorId: 1 },
42
+ { id: 2, title: 'Todo 2', content: 'C2', published: true, authorId: 1 },
43
+ ],
44
+ })
45
+ expect(result.count).toBe(2)
46
+ }),
47
+ )
48
+
49
+ it.effect('createManyAndReturn', () =>
50
+ Effect.gen(function* () {
51
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
52
+
53
+ const result = yield* todoRepo.createManyAndReturn({
54
+ data: [
55
+ { id: 1, title: 'Todo 1', content: 'C1', published: false, authorId: 1 },
56
+ { id: 2, title: 'Todo 2', content: 'C2', published: true, authorId: 1 },
57
+ ],
58
+ })
59
+ expect(result).toHaveLength(2)
60
+ expect(result[0].title).toBe('Todo 1')
61
+ expect(result[1].title).toBe('Todo 2')
62
+ }),
63
+ )
64
+
65
+ it.effect('findUnique', () =>
66
+ Effect.gen(function* () {
67
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
68
+
69
+ yield* todoRepo.create({
70
+ data: { id: 1, title: 'Find Unique', content: 'Content', published: false, authorId: 1 },
71
+ })
72
+
73
+ const found = yield* todoRepo.findUnique({
74
+ where: { id: 1 },
75
+ })
76
+ expect(Option.isSome(found)).toBe(true)
77
+ if (Option.isSome(found)) {
78
+ expect(found.value.title).toBe('Find Unique')
79
+ }
80
+
81
+ const notFound = yield* todoRepo.findUnique({
82
+ where: { id: 999 },
83
+ })
84
+ expect(Option.isNone(notFound)).toBe(true)
85
+ }),
86
+ )
87
+
88
+ it.effect('findUniqueOrThrow', () =>
89
+ Effect.gen(function* () {
90
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
91
+
92
+ yield* todoRepo.create({
93
+ data: { id: 1, title: 'Find Unique Or Throw', content: 'Content', published: false, authorId: 1 },
94
+ })
95
+
96
+ const found = yield* todoRepo.findUniqueOrThrow({
97
+ where: { id: 1 },
98
+ })
99
+ expect(found.title).toBe('Find Unique Or Throw')
100
+
101
+ const error = yield* todoRepo
102
+ .findUniqueOrThrow({
103
+ where: { id: 999 },
104
+ })
105
+ .pipe(Effect.flip)
106
+
107
+ expect(error._tag).toBe('PrismaRecordNotFoundError')
108
+ }),
109
+ )
110
+
111
+ it.effect('findFirst', () =>
112
+ Effect.gen(function* () {
113
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
114
+
115
+ yield* todoRepo.create({
116
+ data: { id: 1, title: 'Find First', content: 'Content', published: false, authorId: 1 },
117
+ })
118
+
119
+ const found = yield* todoRepo.findFirst({
120
+ where: { title: 'Find First' },
121
+ })
122
+ expect(Option.isSome(found)).toBe(true)
123
+ }),
124
+ )
125
+
126
+ it.effect('findFirstOrThrow', () =>
127
+ Effect.gen(function* () {
128
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
129
+
130
+ yield* todoRepo.create({
131
+ data: { id: 1, title: 'Find First Or Throw', content: 'Content', published: false, authorId: 1 },
132
+ })
133
+
134
+ const found = yield* todoRepo.findFirstOrThrow({
135
+ where: { title: 'Find First Or Throw' },
136
+ })
137
+ expect(found.title).toBe('Find First Or Throw')
138
+
139
+ const error = yield* todoRepo
140
+ .findFirstOrThrow({
141
+ where: { title: 'Non Existent' },
142
+ })
143
+ .pipe(Effect.flip)
144
+
145
+ expect(error._tag).toBe('PrismaRecordNotFoundError')
146
+ }),
147
+ )
148
+
149
+ it.effect('findMany', () =>
150
+ Effect.gen(function* () {
151
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
152
+
153
+ yield* todoRepo.createMany({
154
+ data: [
155
+ { id: 1, title: 'T1', content: 'C', published: true, authorId: 1 },
156
+ { id: 2, title: 'T2', content: 'C', published: true, authorId: 1 },
157
+ { id: 3, title: 'T3', content: 'C', published: false, authorId: 1 },
158
+ ],
159
+ })
160
+
161
+ const found = yield* todoRepo.findMany({
162
+ where: { published: true },
163
+ })
164
+ expect(found).toHaveLength(2)
165
+ }),
166
+ )
167
+
168
+ it.effect('update', () =>
169
+ Effect.gen(function* () {
170
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
171
+
172
+ yield* todoRepo.create({
173
+ data: { id: 1, title: 'Original', content: 'Content', published: false, authorId: 1 },
174
+ })
175
+
176
+ const updated = yield* todoRepo.update({
177
+ where: { id: 1 },
178
+ data: { title: 'Updated' },
179
+ })
180
+ expect(updated.title).toBe('Updated')
181
+ }),
182
+ )
183
+
184
+ it.effect('updateMany', () =>
185
+ Effect.gen(function* () {
186
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
187
+
188
+ yield* todoRepo.createMany({
189
+ data: [
190
+ { id: 1, title: 'T1', content: 'C', published: false, authorId: 1 },
191
+ { id: 2, title: 'T2', content: 'C', published: false, authorId: 1 },
192
+ ],
193
+ })
194
+
195
+ const result = yield* todoRepo.updateMany({
196
+ where: { published: false },
197
+ data: { published: true },
198
+ })
199
+ expect(result.count).toBe(2)
200
+ }),
201
+ )
202
+
203
+ it.effect('upsert', () =>
204
+ Effect.gen(function* () {
205
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
206
+
207
+ // Create new
208
+ const created = yield* todoRepo.upsert({
209
+ where: { id: 1 },
210
+ create: { id: 1, title: 'Upsert Created', content: 'Content', published: false, authorId: 1 },
211
+ update: { title: 'Upsert Updated' },
212
+ })
213
+ expect(created.title).toBe('Upsert Created')
214
+
215
+ // Update existing
216
+ const updated = yield* todoRepo.upsert({
217
+ where: { id: 1 },
218
+ create: { id: 1, title: 'Upsert Created', content: 'Content', published: false, authorId: 1 },
219
+ update: { title: 'Upsert Updated' },
220
+ })
221
+ expect(updated.title).toBe('Upsert Updated')
222
+ }),
223
+ )
224
+
225
+ it.effect('delete', () =>
226
+ Effect.gen(function* () {
227
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
228
+
229
+ yield* todoRepo.create({
230
+ data: { id: 1, title: 'To Delete', content: 'Content', published: false, authorId: 1 },
231
+ })
232
+
233
+ const deleted = yield* todoRepo.delete({
234
+ where: { id: 1 },
235
+ })
236
+ expect(deleted.title).toBe('To Delete')
237
+
238
+ const found = yield* todoRepo.findUnique({
239
+ where: { id: 1 },
240
+ })
241
+ expect(Option.isNone(found)).toBe(true)
242
+ }),
243
+ )
244
+
245
+ it.effect('deleteMany', () =>
246
+ Effect.gen(function* () {
247
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
248
+
249
+ yield* todoRepo.createMany({
250
+ data: [
251
+ { id: 1, title: 'T1', content: 'C', published: false, authorId: 1 },
252
+ { id: 2, title: 'T2', content: 'C', published: false, authorId: 1 },
253
+ ],
254
+ })
255
+
256
+ const result = yield* todoRepo.deleteMany({
257
+ where: { published: false },
258
+ })
259
+ expect(result.count).toBe(2)
260
+ }),
261
+ )
262
+
263
+ it.effect('count', () =>
264
+ Effect.gen(function* () {
265
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
266
+
267
+ yield* todoRepo.createMany({
268
+ data: [
269
+ { id: 1, title: 'T1', content: 'C', published: false, authorId: 1 },
270
+ { id: 2, title: 'T2', content: 'C', published: false, authorId: 1 },
271
+ ],
272
+ })
273
+
274
+ const count = yield* todoRepo.count()
275
+ expect(count).toBe(2)
276
+
277
+ const filteredCount = yield* todoRepo.count({
278
+ where: { id: 1 },
279
+ })
280
+ expect(filteredCount).toBe(1)
281
+ }),
282
+ )
283
+
284
+ it.effect('aggregate', () =>
285
+ Effect.gen(function* () {
286
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
287
+
288
+ yield* todoRepo.createMany({
289
+ data: [
290
+ { id: 1, title: 'T1', content: 'C', published: false, authorId: 1 },
291
+ { id: 2, title: 'T2', content: 'C', published: false, authorId: 1 },
292
+ ],
293
+ })
294
+
295
+ const result: any = yield* todoRepo.aggregate({
296
+ _count: true,
297
+ _min: { id: true },
298
+ _max: { id: true },
299
+ })
300
+
301
+ expect(result._count).toBe(2)
302
+ expect(result._min.id).toBe(1)
303
+ expect(result._max.id).toBe(2)
304
+ }),
305
+ )
306
+
307
+ it.effect('groupBy', () =>
308
+ Effect.gen(function* () {
309
+ const todoRepo = yield* PrismaRepository.make(TodoModel, { modelName: 'todo', spanPrefix: 'todo' })
310
+
311
+ yield* todoRepo.createMany({
312
+ data: [
313
+ { id: 1, title: 'T1', content: 'C', published: true, authorId: 1 },
314
+ { id: 2, title: 'T2', content: 'C', published: true, authorId: 1 },
315
+ { id: 3, title: 'T3', content: 'C', published: false, authorId: 2 },
316
+ ],
317
+ })
318
+
319
+ const result: any = yield* todoRepo.groupBy({
320
+ by: ['published', 'authorId'],
321
+ _count: {
322
+ id: true,
323
+ },
324
+ })
325
+
326
+ expect(Array.isArray(result)).toBe(true)
327
+ expect(result.length).toBeGreaterThan(0)
328
+
329
+ // Check for the group with published: true, authorId: 1
330
+ const group1 = result.find((g: any) => g.published === true && g.authorId === 1)
331
+ expect(group1).toBeDefined()
332
+ expect(group1._count.id).toBe(2)
333
+
334
+ // Check for the group with published: false, authorId: 2
335
+ const group2 = result.find((g: any) => g.published === false && g.authorId === 2)
336
+ expect(group2).toBeDefined()
337
+ expect(group2._count.id).toBe(1)
338
+ }),
339
+ )
340
+ })
package/test/utils.ts ADDED
@@ -0,0 +1,10 @@
1
+ import path from 'node:path'
2
+ import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'
3
+ import { PrismaClient } from '@prisma/client'
4
+
5
+ const dbPath = path.join(__dirname, '../prisma/dev.db')
6
+ const adapter = new PrismaBetterSqlite3({
7
+ url: `file:${dbPath}`,
8
+ })
9
+
10
+ export const prisma = new PrismaClient({ adapter })
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "files": [],
4
+ "include": [],
5
+ "references": [
6
+ {
7
+ "path": "./tsconfig.lib.json"
8
+ },
9
+ {
10
+ "path": "./tsconfig.spec.json"
11
+ }
12
+ ],
13
+ "compilerOptions": {
14
+ "baseUrl": ".",
15
+ "paths": {
16
+ "@prisma/client": ["node_modules/@prisma/client"],
17
+ "@prisma/*": ["./prisma/generated/*"]
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "baseUrl": ".",
6
+ "rootDir": "src",
7
+ "outDir": "dist",
8
+ "target": "es2022",
9
+ "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
10
+ "emitDeclarationOnly": false,
11
+ "moduleResolution": "nodenext",
12
+ "forceConsistentCasingInFileNames": true,
13
+ "types": ["node"],
14
+ "paths": {
15
+ "@/*": ["./src/*"],
16
+ "@prisma/client": ["node_modules/@prisma/client"],
17
+ "@prisma/effect": ["./prisma/generated/effect/index.ts"],
18
+ "@prisma/effect/*": ["./prisma/generated/effect/*"],
19
+ "@prisma/*": ["./prisma/generated/*"]
20
+ }
21
+ },
22
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
23
+ "references": []
24
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/out-tsc",
5
+ "types": ["vitest/globals", "node"],
6
+ "baseUrl": ".",
7
+ "paths": {
8
+ "@prisma/client": ["node_modules/@prisma/client"],
9
+ "@prisma/effect": ["./prisma/generated/effect/index.ts"],
10
+ "@prisma/effect/*": ["./prisma/generated/effect/*"],
11
+ "@prisma/*": ["./prisma/generated/*"]
12
+ }
13
+ },
14
+ "include": ["test/**/*.ts", "src/**/*.ts", "prisma/generated/**/*.ts"]
15
+ }
@@ -0,0 +1,23 @@
1
+ import path from 'node:path'
2
+ import tsconfigPaths from 'vite-tsconfig-paths'
3
+ import { defineConfig } from 'vitest/config'
4
+
5
+ export default defineConfig({
6
+ plugins: [tsconfigPaths()],
7
+ test: {
8
+ setupFiles: [path.join(__dirname, 'setup-tests.ts')],
9
+ include: ['**/*.test.ts'],
10
+ globals: true,
11
+ },
12
+ resolve: {
13
+ alias: {
14
+ '@template/basic/test': path.join(__dirname, 'test'),
15
+ '@template/basic': path.join(__dirname, 'src'),
16
+ '@prisma/effect': path.join(__dirname, 'prisma/generated/effect'),
17
+ },
18
+ extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
19
+ },
20
+ esbuild: {
21
+ target: 'node22',
22
+ },
23
+ })