@shivaedev/effect-prisma 0.8.0 → 0.9.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 (81) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +198 -155
  3. package/dist/database.d.ts +5 -5
  4. package/dist/database.d.ts.map +1 -1
  5. package/dist/database.js +12 -8
  6. package/dist/database.js.map +1 -1
  7. package/dist/databaseTypes.d.ts +17 -17
  8. package/dist/databaseTypes.d.ts.map +1 -1
  9. package/dist/error.d.ts.map +1 -1
  10. package/dist/internal/adapters/transaction-settlement.d.ts.map +1 -1
  11. package/dist/internal/adapters/transaction-settlement.js +9 -7
  12. package/dist/internal/adapters/transaction-settlement.js.map +1 -1
  13. package/dist/internal/client-lifecycle.d.ts.map +1 -1
  14. package/dist/internal/contract-normalization.d.ts.map +1 -1
  15. package/dist/internal/database-facade.d.ts +11 -11
  16. package/dist/internal/database-facade.d.ts.map +1 -1
  17. package/dist/internal/database-facade.js.map +1 -1
  18. package/dist/internal/database-factory.d.ts +1 -1
  19. package/dist/internal/database-factory.d.ts.map +1 -1
  20. package/dist/internal/database-factory.js +2 -2
  21. package/dist/internal/database-factory.js.map +1 -1
  22. package/dist/internal/dynamic.d.ts.map +1 -1
  23. package/dist/internal/dynamic.js +3 -1
  24. package/dist/internal/dynamic.js.map +1 -1
  25. package/dist/internal/executor.d.ts +6 -6
  26. package/dist/internal/executor.d.ts.map +1 -1
  27. package/dist/internal/promise.d.ts.map +1 -1
  28. package/dist/internal/query-execution.d.ts.map +1 -1
  29. package/dist/internal/recipe.d.ts.map +1 -1
  30. package/dist/internal/recipe.js +10 -10
  31. package/dist/internal/recipe.js.map +1 -1
  32. package/dist/internal/relation-plan.d.ts.map +1 -1
  33. package/dist/internal/relation-result.d.ts.map +1 -1
  34. package/dist/internal/relation-result.js +6 -2
  35. package/dist/internal/relation-result.js.map +1 -1
  36. package/dist/internal/relation-runtime.d.ts +1 -1
  37. package/dist/internal/relation-runtime.d.ts.map +1 -1
  38. package/dist/internal/relation-runtime.js +11 -6
  39. package/dist/internal/relation-runtime.js.map +1 -1
  40. package/dist/internal/relation-stream.d.ts +1 -1
  41. package/dist/internal/relation-stream.d.ts.map +1 -1
  42. package/dist/internal/relation-stream.js +9 -7
  43. package/dist/internal/relation-stream.js.map +1 -1
  44. package/dist/internal/sqlite-datetime.d.ts.map +1 -1
  45. package/dist/internal/sqlite-datetime.js +11 -9
  46. package/dist/internal/sqlite-datetime.js.map +1 -1
  47. package/dist/internal/sqlite-pragmas.d.ts.map +1 -1
  48. package/dist/internal/testing.d.ts +5 -5
  49. package/dist/internal/testing.d.ts.map +1 -1
  50. package/dist/internal/testing.js.map +1 -1
  51. package/dist/internal/transaction.d.ts +6 -6
  52. package/dist/internal/transaction.d.ts.map +1 -1
  53. package/dist/internal/transaction.js +5 -3
  54. package/dist/internal/transaction.js.map +1 -1
  55. package/dist/relation/include.d.ts +20 -20
  56. package/dist/relation/include.d.ts.map +1 -1
  57. package/dist/relation.d.ts +23 -23
  58. package/dist/relation.d.ts.map +1 -1
  59. package/dist/sqlite.d.ts.map +1 -1
  60. package/dist/sqlite.js +8 -6
  61. package/dist/sqlite.js.map +1 -1
  62. package/docs/north-star.md +47 -0
  63. package/docs/roadmap.md +32 -0
  64. package/package.json +27 -23
  65. package/src/database.ts +29 -23
  66. package/src/databaseTypes.ts +31 -31
  67. package/src/internal/adapters/transaction-settlement.ts +9 -8
  68. package/src/internal/database-facade.ts +27 -27
  69. package/src/internal/database-factory.ts +12 -12
  70. package/src/internal/dynamic.ts +3 -1
  71. package/src/internal/executor.ts +6 -6
  72. package/src/internal/recipe.ts +10 -10
  73. package/src/internal/relation-result.ts +6 -3
  74. package/src/internal/relation-runtime.ts +25 -20
  75. package/src/internal/relation-stream.ts +5 -4
  76. package/src/internal/sqlite-datetime.ts +11 -9
  77. package/src/internal/testing.ts +8 -8
  78. package/src/internal/transaction.ts +16 -15
  79. package/src/relation/include.ts +48 -48
  80. package/src/relation.ts +38 -38
  81. package/src/sqlite.ts +8 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.1 - 2026-10-17
4
+
5
+ ### Fixed
6
+
7
+ - Publish against `@shivaedev/effect-test` 0.5.0 so framework consumers share the current test runner.
8
+
9
+ ## 0.9.0 - 2026-10-13
10
+
11
+ ### Changed
12
+
13
+ - Use `T`-prefixed descriptive type parameter names in Relation types and internal testing and transaction helpers, preserving their type behavior.
14
+ - Document the package purpose, query and transaction model, API and limits; add agent guidance and package north star and roadmap, and publish `docs/` with the README.
15
+ - Build package JavaScript, declarations and source maps with TypeScript 7.
16
+
17
+ ### Breaking
18
+
19
+ - Requires stable Effect 4.0.4 in place of 4.0.0-rc.112. Effect 4.0.4 moves the former `effect/unstable/*` modules to `effect/*` and renames constructors such as `Config.string` to `Config.String`. Install `@effect/vitest` 4.0.4 alongside it, which requires Vitest 5.
20
+
3
21
  ## 0.8.0 - 2026-10-04
4
22
 
5
23
  ### Changed
package/README.md CHANGED
@@ -1,234 +1,277 @@
1
- # `@shivaedev/effect-prisma`
1
+ # @shivaedev/effect-prisma
2
2
 
3
- Effect-native PostgreSQL queries and transactions backed by Prisma Next.
3
+ Use Prisma Next's generated model queries in Effect, with typed rows, database transactions and tests that roll their writes back. Keep the contract your application already owns while making database work compose with its Effect services.
4
4
 
5
- This package currently targets exact early-access versions of Effect v4 and
6
- Prisma Next. It is built for ShivaeDev applications and may change without a
7
- deprecation period.
5
+ ## Why you want this
8
6
 
9
- ## Install
7
+ A Promise-based database client leaves every Effect application to rebuild the same boundary: translate failures, pass the transaction client through its services and clean up test writes. This package gives that work one Database service:
10
8
 
11
- ```sh
12
- pnpm add @shivaedev/effect-prisma effect
9
+ ```ts
10
+ const createUser = Effect.gen(function* () {
11
+ const db = yield* Database;
12
+ return yield* db.transaction(
13
+ Effect.gen(function* () {
14
+ const tx = yield* Database;
15
+ return yield* tx.User.create({
16
+ id: crypto.randomUUID(),
17
+ email: "ada@example.test",
18
+ name: "Ada",
19
+ });
20
+ }),
21
+ );
22
+ });
13
23
  ```
14
24
 
15
- Generate a Prisma Next contract in the application, then create its database
16
- service:
25
+ `Database` is your application's exported definition. The transaction body yields that same service and uses the same generated models. A successful body commits; a failed body rolls back. The test harness runs that work in a rollback-only transaction, so a test can create real rows without leaving them behind.
17
26
 
18
- ```sh
19
- prisma-next contract emit
20
- effect-prisma-normalize path/to/generated/contract.d.ts
21
- ```
27
+ ## Using it
28
+
29
+ ### How to think about it
22
30
 
23
- The normalization step is idempotent and currently required because Prisma
24
- Next's generated PostgreSQL timestamp output declarations do not match its
25
- runtime `Date` codecs. It fails when Prisma emits an unfamiliar timestamp shape
26
- instead of silently producing weakened types.
31
+ There are three things to keep separate:
32
+
33
+ - The **contract** is Prisma Next's description of your database and generated TypeScript model types.
34
+ - A **Database definition** is an Effect service for that contract. Its Layer provides a live database facade.
35
+ - A **Relation** is a reusable model query, such as `db.User.where({ name: "Ada" })`. It is also an Effect: yielding a collection Relation returns its rows.
36
+
37
+ A Relation keeps the calls that describe its query. Branching it creates another query without changing the base. A root Relation resolves the active transaction when it runs; one made from a transaction facade belongs to that transaction's lifetime and context. You do not provide a separate executor service to queries or Streams.
38
+
39
+ Organize application work around these boundaries: define the Database and its Layer once, write reusable queries in each feature's repository, and run those Effects or rollback tests every day. The examples use a contract with `User` and `Post` models: users have `id`, `email`, `name`, `createdAt`, `verifiedAt` and `posts`; posts have `title`, `user` and an optional `reviewer`.
40
+
41
+ ### Once per application: define the database
27
42
 
28
43
  ```ts
29
- import { makeDatabase } from "@shivaedev/effect-prisma/database.ts"
30
- import { contractJson, type Contract } from "./generated/contract.js"
44
+ import { makeDatabase } from "@shivaedev/effect-prisma/database.ts";
45
+ import type { Contract } from "./generated/contract.d.ts";
46
+ import contractJson from "./generated/contract.json" with { type: "json" };
31
47
 
32
48
  export const Database = makeDatabase<Contract>()("@app/Database", {
33
49
  contractJson,
34
- })
50
+ });
35
51
 
36
52
  export const DatabaseLive = Database.layer({
37
- url: process.env.DATABASE_URL!,
38
- })
53
+ url: "postgresql://localhost/application",
54
+ });
39
55
  ```
40
56
 
41
- Each identifier names one exported Database definition, and each composed
42
- runtime provides one live Layer for that definition. Reuse that singleton;
43
- duplicate identifiers and multiple simultaneous Layers for one definition are
44
- outside the supported composition.
45
-
46
- The Layer owns the PostgreSQL client and closes it with its structured Effect
47
- scope. Database values do not outlive that scope.
57
+ The type parameter is the generated contract type; `contractJson` is its emitted JSON. Choose one literal identifier for each exported definition. Different identifiers keep two databases distinct even when they use the same contract, including in the types of included queries. Widened strings, unions and template patterns cannot identify one Database.
48
58
 
49
- ## SQLite (experimental)
59
+ Reuse the definition and provide one live Layer for it in a composed runtime. Keep values within that Layer's scope: a root Stream or Database facade used after the owning Layer closes fails with `RUNTIME.DATABASE_CLOSED`. A transaction-bound value instead closes with its transaction and fails with `RUNTIME.TRANSACTION_CLOSED` after settlement.
50
60
 
51
- A SQLite contract emitted with `@prisma-next/sqlite` uses the same API through the
52
- separate `sqlite.ts` module. Its `DateTime` fields already declare `Date`, so the
53
- normalization step above is PostgreSQL-only.
61
+ SQLite uses the same query and transaction facade through its own factory:
54
62
 
55
63
  ```ts
56
- import { makeSqliteDatabase } from "@shivaedev/effect-prisma/sqlite.ts"
57
- import { contractJson, type Contract } from "./generated/contract.js"
64
+ import { makeSqliteDatabase } from "@shivaedev/effect-prisma/sqlite.ts";
65
+ import type { Contract } from "./generated/contract.d.ts";
66
+ import contractJson from "./generated/contract.json" with { type: "json" };
58
67
 
59
68
  export const Database = makeSqliteDatabase<Contract>()("@app/Database", {
60
69
  contractJson,
61
- })
70
+ });
62
71
 
63
- export const DatabaseLive = Database.layer({
64
- path: "app.db",
65
- })
72
+ export const DatabaseLive = Database.layer({ path: "application.db" });
66
73
  ```
67
74
 
68
- The Layer applies `journal_mode=WAL` when it connects; pass `pragmas` to change
69
- that list. Prisma Next's SQLite driver already sets `foreign_keys` and
70
- `busy_timeout` on every connection it opens, and it opens a separate connection
71
- per transaction, so in-memory databases are rejected.
75
+ Use a SQLite contract for this definition. The default connect-time pragma sets `journal_mode=WAL`. SQLite's serialization and file requirements are described under limits below.
72
76
 
73
- That driver is synchronous. Queries block the event loop while they run, and
74
- because SQLite allows a single writer, overlapping write transactions wait for
75
- `busy_timeout` while blocking that event loop. The supported Database Layer
76
- therefore serializes finite root query effects and explicit transaction
77
- lifetimes through one access permit. Transaction-internal queries use a private
78
- permit for their single connection, while root Streams remain incremental. Do
79
- not coordinate multiple Database Layers against the same SQLite file.
77
+ ### Once per feature: write reusable queries
80
78
 
81
- SQLite stores `DateTime` as text, and `prisma-next db init` generates
82
- `DEFAULT (datetime('now'))`, which writes a UTC instant without a zone
83
- designator. The module decodes zone-less datetime text as UTC, so generated
84
- column defaults round-trip to the instant SQLite wrote without hand-editing the
85
- DDL. Values that already carry `Z` or a numeric offset decode unchanged.
86
- `datetime('now')` itself stores whole seconds; write
87
- `strftime('%Y-%m-%dT%H:%M:%fZ', 'now')` where a default needs milliseconds.
79
+ ```ts
80
+ import { Effect } from "effect";
81
+ import { Database } from "./database.ts";
82
+
83
+ export const findUsersNamed = (name: string) =>
84
+ Effect.gen(function* () {
85
+ const db = yield* Database;
86
+ const matching = db.User.where({ name });
87
+ const names = matching.orderBy((user) => user.email.asc()).select("id", "email");
88
+
89
+ return {
90
+ rows: yield* names.take(20),
91
+ first: yield* matching.first(),
92
+ exists: yield* matching.exists(),
93
+ count: yield* matching.count(),
94
+ };
95
+ });
96
+ ```
88
97
 
89
- ## Queries
98
+ `matching` remains the full filtered Relation after `names` adds an order and selection. A selection keeps only its named fields in the result type. `first()` returns an Effect `Option`, `exists()` returns a boolean and `count()` returns a number. Object filters and callback filters retain the contract's field types.
90
99
 
91
- Yield the database service once and use generated models directly:
100
+ Load relations by their contract names:
92
101
 
93
102
  ```ts
94
- import { Effect } from "effect"
103
+ export const userPostOverview = Effect.gen(function* () {
104
+ const db = yield* Database;
105
+ const titles = db.Post.orderBy((post) => post.title.asc()).select("title");
106
+ const page = titles.take(1);
107
+
108
+ return yield* db.User.include("posts", {
109
+ items: page,
110
+ fullCount: db.Post.count(),
111
+ pageCount: page.count(),
112
+ });
113
+ });
114
+ ```
95
115
 
96
- const program = Effect.gen(function* () {
97
- const db = yield* Database
116
+ The `posts` property contains the named projections: a page of titles, the full related count and the page count. `include("posts")` loads the related rows directly; passing a Relation refines them. Includes can nest, as in `db.User.include("posts", db.Post.include("user"))`, and chain, as in `db.Post.include("user").include("reviewer")`. To-many values are arrays; an optional reviewer stays `User | null`.
98
117
 
99
- const active = db.User.where({ active: true })
100
- const newest = active.orderBy((user) => user.createdAt.desc())
118
+ An included query must come from the same Database and use the related model. A named query record must be nonempty and applies only to a to-many relation. The compiler rejects these mismatches; runtime checks also reject foreign databases, wrong models and transaction-bound queries used outside their owner.
101
119
 
102
- const users = yield* newest.take(20)
103
- const first = yield* active.first()
104
- const exists = yield* active.exists()
105
- const count = yield* active.count()
120
+ For grouped summaries and bulk changes, use the generated collection operations:
106
121
 
107
- return { users, first, exists, count }
108
- })
122
+ ```ts
123
+ export const renameUsers = (name: string, replacement: string) =>
124
+ Effect.gen(function* () {
125
+ const db = yield* Database;
126
+ const matching = db.User.where({ name });
127
+ const summary = yield* matching.aggregate((aggregate) => ({
128
+ total: aggregate.count(),
129
+ }));
130
+ const groups = yield* matching.groupBy("name").aggregate((aggregate) => ({
131
+ total: aggregate.count(),
132
+ }));
133
+ const changed = yield* matching.updateAll({ name: replacement });
134
+ return { summary, groups, changed };
135
+ });
109
136
  ```
110
137
 
111
- Relations are lazy and immutable. Reusing or branching a Relation never changes
112
- the original, and each Relation closes over the Database implementation that
113
- created it.
138
+ `aggregate` returns the named summary; grouped aggregates also include the grouped fields. `createAll`, `updateAll` and `deleteAll` return rows. Update and delete methods require an explicit filter in the typed API.
114
139
 
115
- Relations can be loaded directly or refined with another immutable Relation:
140
+ ### Every day: run effects and transactions
116
141
 
117
142
  ```ts
118
- const recentPosts = db.Post
119
- .orderBy((post) => post.createdAt.desc())
120
- .take(5)
143
+ const result = await Effect.runPromise(
144
+ findUsersNamed("Ada").pipe(Effect.provide(DatabaseLive)),
145
+ );
146
+ ```
121
147
 
122
- const usersWithPosts = yield* db.User.include("posts", recentPosts)
148
+ Provide the Database Layer to the program that yields its definition. Repository queries remain ordinary Effects that a larger application's Layer can provide.
123
149
 
124
- const usersWithPostCounts = yield* db.User.include("posts", db.Post.count())
125
- ```
150
+ A transaction body must yield its Database definition. The boundary provides that service for the body while retaining any other service requirements. Nested package transactions reuse the active transaction and its facade; they do not create independent rollback boundaries.
126
151
 
127
- Includes can be chained and nested. Loaded to-many relations are arrays, and
128
- nullable to-one relations remain nullable in the result type.
152
+ A root query captured before a transaction participates when run inside it. A Database, Relation or Stream created from the transaction facade cannot be used after settlement, outside that transaction or in a concurrent sibling transaction. Closed values fail with `RUNTIME.TRANSACTION_CLOSED`; values used in another active context fail with `RUNTIME.TRANSACTION_CONTEXT_MISMATCH`.
129
153
 
130
- Named query records return several projections of the same relation:
154
+ Successful transactions commit. Typed failures and interruption roll back before returning. Queries sharing the transaction connection execute one at a time. Once such a query starts, interruption waits for it to settle before another query or transaction settlement uses the connection.
155
+
156
+ Collection Relations also expose reusable Streams:
131
157
 
132
158
  ```ts
133
- const publishedPosts = db.Post.where({ published: true })
134
- const recentPublishedPosts = publishedPosts
135
- .orderBy((post) => post.createdAt.desc())
136
- .take(5)
137
-
138
- const usersWithPostOverview = yield* db.User.include("posts", {
139
- items: recentPublishedPosts,
140
- fullCount: publishedPosts.count(),
141
- })
159
+ import { Stream } from "effect";
160
+
161
+ export const userEmails = Effect.gen(function* () {
162
+ const db = yield* Database;
163
+ return yield* Stream.runCollect(
164
+ db.User.select("email").stream.pipe(Stream.map((user) => user.email)),
165
+ );
166
+ });
142
167
  ```
143
168
 
144
- Collections are also cold Effect Streams:
169
+ Outside transactions the source emits incrementally. Inside a transaction the package buffers the source before downstream work runs, so a downstream database query can use the same connection. Account for the full result's memory use in a transaction.
170
+
171
+ ### Every day: test real writes with rollback
172
+
173
+ Define the application's database test harness once:
145
174
 
146
175
  ```ts
147
- const stream = db.User.where({ active: true }).stream
176
+ import { makeDatabaseIt } from "@shivaedev/effect-prisma/testing/vitest.ts";
177
+ import { Database, DatabaseTest } from "./database-test.ts";
178
+
179
+ export const it = makeDatabaseIt({
180
+ database: Database,
181
+ layer: DatabaseTest,
182
+ });
148
183
  ```
149
184
 
150
- The package covers scalar models, filtering, selection, relation loading,
151
- ordering, pagination, distinct queries, aggregates, grouping, and
152
- create/update/delete terminals. Model variants are deliberately deferred rather
153
- than exposed with weakened types.
185
+ `DatabaseTest` is the application's Layer for an initialized test database. A test receives the typed facade and Vitest context:
154
186
 
155
- PostgreSQL timestamp and timestamp-with-time-zone fields use JavaScript `Date`
156
- values for reads, writes, filters, selections, includes, and Streams after the
157
- generated contract is normalized.
187
+ ```ts
188
+ import { expect } from "vitest";
189
+ import { it } from "./database-it.ts";
158
190
 
159
- ## Transactions
191
+ it.effectDB("creates a user", function* (db, context) {
192
+ const email = `${crypto.randomUUID()}@example.test`;
193
+ const user = yield* db.User.create({
194
+ id: crypto.randomUUID(),
195
+ email,
196
+ name: "Ada",
197
+ });
160
198
 
161
- Transactions keep the same database API and are implicitly scoped:
199
+ expect(user.email).toBe(email);
200
+ expect(context.task.name).toContain("creates a user");
201
+ });
202
+ ```
203
+
204
+ Successful and failing test bodies both roll back their writes. `effectDB.each` takes each table item before the database and context; `effectDB.fails` declares an expected failure. Ordinary transactions nested inside a rollback test share its scope and are rolled back with it.
205
+
206
+ For another test runner, wrap an Effect with the rollback primitive:
162
207
 
163
208
  ```ts
164
- yield* db.transaction(
209
+ import { withTestTransaction } from "@shivaedev/effect-prisma/testing/transaction.ts";
210
+
211
+ const testProgram = withTestTransaction(
212
+ Database,
165
213
  Effect.gen(function* () {
166
- const db = yield* Database
167
- yield* db.User.create({
168
- id,
169
- email,
170
- name,
171
- })
214
+ const db = yield* Database;
215
+ return yield* db.User.count();
172
216
  }),
173
- )
217
+ ).pipe(Effect.provide(DatabaseTest));
174
218
  ```
175
219
 
176
- Relations from the singleton Database resolve its active transaction when they
177
- execute, so a Relation created before the boundary still participates when used
178
- inside it. The Database yielded inside the body is the transaction-bound
179
- implementation for methods constructed there. A transaction-bound Database or
180
- Relation is valid only inside that boundary and fails closed if used after
181
- settlement.
220
+ The primitive returns the body's value or preserves its failure while rolling back. A rollback-only boundary inside an ordinary commit transaction is refused with `RUNTIME.TEST_TRANSACTION_INSIDE_TRANSACTION_UNSUPPORTED`; nesting another boundary inside an existing rollback test shares that test's transaction.
221
+
222
+ ### API
223
+
224
+ Import the defining module; the package has no root entry point.
182
225
 
183
- Nested package transactions reuse the active transaction. Successful programs
184
- commit; failure, defect, and interruption roll back. A forced-rollback test
185
- transaction inside an ordinary commit transaction is refused because the
186
- drivers do not provide the savepoint semantics that promise would require.
226
+ | Module | Public surface |
227
+ | --- | --- |
228
+ | `database.ts` | `makeDatabase<Contract>()(identifier, { contractJson })`, `DatabaseDefinition`, `DatabaseLayerOptions`. Factory options also accept an already typed `contract`. |
229
+ | `sqlite.ts` | `makeSqliteDatabase<Contract>()(identifier, options)`, `defaultSqlitePragmas`, `SqliteDatabaseDefinition`, `SqliteDatabaseLayerOptions`. Layer options include `path` and `pragmas`. |
230
+ | `databaseTypes.ts` | `DatabaseService<Contract, Identifier>`, `DatabaseServiceOf<typeof Database>`, `AnyDatabase`, `DatabaseIdentifier`, `DatabaseIdentifierLiteral`, `DatabaseServiceHolder`, `DefaultModels`, `SqlDatabase`, `internalContextIdentifierPrefix`. |
231
+ | `relation.ts` | `Relation`, `RelationQuery`, `CollectionResult`. |
232
+ | `error.ts` | `PrismaError`, `PrismaErrorReason`, `PrismaRuntimeFailure`, `PrismaQueryFailure`, `PrismaConnectionFailure`, `isPrismaFailure`, `toPrismaError`. |
233
+ | `testing/vitest.ts` | `makeDatabaseIt({ database, layer, clock? })`. |
234
+ | `testing/transaction.ts` | `withTestTransaction(database, program)` and `withTestTransaction(database)(program)`. |
235
+ | `testing/types.ts` | `DatabaseIt`, `DatabaseTester`, `DatabaseTest`, `DatabaseService`, `MakeDatabaseItOptions`. |
236
+ | `relation/include.ts`, `relation/include-metadata.ts`, `relation/prisma-methods.ts` | Supporting types: `IncludeMethod`, `AnyPostgresContract`, `IsToManyRelation`, `RelatedModelNameOf`, `IncludedRelationValue`, `PrismaRelationMethods`. |
237
+ | `effect-prisma-normalize` | Executable taking one generated contract declaration path. |
187
238
 
188
- Queries composed concurrently inside a transaction are executed one at a time
189
- on its single connection. Once a transaction query starts, interruption waits
190
- for it to settle before releasing that connection. PostgreSQL query effects
191
- outside transactions remain parallel. SQLite root query effects execute one at
192
- a time and do not overlap an explicit transaction lifetime, so a root write
193
- cannot invalidate the transaction's WAL snapshot.
239
+ The Relation surface follows Prisma Next's collections. Its typed operations include filters and ordering; `select`, `take`, `cursor`, `distinct`, `distinctOn`; `include`, `count`, `exists`, `first`; `aggregate`, `groupBy`; and create, update and delete terminals. Cursor and distinct-on types require an order first. Import boolean filter combinators such as `and`, `or`, `not` and `all` from `@prisma-next/sql-orm-client`.
194
240
 
195
- Transaction-scoped Streams are read into memory before emitting rows. This
196
- releases the connection before downstream Stream effects run database queries.
197
- Streams outside transactions retain incremental fetching.
241
+ A `PrismaError` carries a tagged `reason`. Unique-constraint tests verify `PrismaQueryFailure` with `sqlState` and `constraint`; scope and transaction guards use `PrismaRuntimeFailure` with `code`. Unknown Promise rejections remain defects rather than becoming typed database failures.
198
242
 
199
- ## Vitest
243
+ `effectDB` also exposes the declared `skip`, `skipIf`, `runIf` and `only` methods. Its options and factory accept `clock`; see the [Effect test runner](https://github.com/ShivaeDev/platform/tree/main/packages/effect-test#readme) for the shared option types and clock model.
200
244
 
201
- Install `@effect/vitest` to use the optional `testing/` modules:
245
+ ### Install and contract setup
202
246
 
203
247
  ```sh
204
- pnpm add --save-dev @effect/vitest vitest
248
+ pnpm add @shivaedev/effect-prisma effect@4.0.4
249
+ pnpm add --save-dev @effect/vitest@4.0.4 vitest@5.0.3
205
250
  ```
206
251
 
207
- Configure the database once in the application's test support:
252
+ The Vitest dependencies are needed only for `testing/vitest.ts`. Use Node.js 24 or newer and compatible versions of the package's Effect and Prisma Next dependencies.
208
253
 
209
- ```ts
210
- import { makeDatabaseIt } from "@shivaedev/effect-prisma/testing/vitest.ts"
254
+ The application owns its Prisma Next CLI/configuration, initialized schema and
255
+ migration workflow. This package does not apply schema changes. Emit that
256
+ application's contract, then normalize its PostgreSQL declaration:
211
257
 
212
- export const it = makeDatabaseIt({
213
- database: Database,
214
- layer: DatabaseTest,
215
- })
258
+ ```sh
259
+ prisma-next contract emit
260
+ effect-prisma-normalize path/to/generated/contract.d.ts
216
261
  ```
217
262
 
218
- Tests receive the typed database facade and always roll back:
263
+ Normalization replaces supported PostgreSQL timestamp declarations with `Date`, preserves other fields and can run twice. An unsupported timestamp declaration fails without overwriting the generated file. PostgreSQL timestamp and timestamp-with-time-zone fields accept and return JavaScript `Date` values; the tests exercise writes, reads and equality filters. SQLite `DateTime` declarations already use `Date` and do not need this PostgreSQL step.
219
264
 
220
- ```ts
221
- it.effectDB("creates a user", function* (db) {
222
- const user = yield* db.User.create({
223
- id,
224
- email,
225
- name,
226
- })
265
+ SQLite datetime text without a zone is decoded as UTC, including the `datetime('now')` column default. Text that already contains `Z` or a numeric offset is left unchanged. That generated default has whole-second precision.
227
266
 
228
- expect(user.email).toBe(email)
229
- })
230
- ```
267
+ ### Limits
268
+
269
+ - Use one exported definition and one live Layer per identifier. Duplicate identifiers or multiple simultaneous Layers for one definition are outside the supported composition.
270
+ - Keep Database values inside their Layer and transaction lifetimes. Return rows from a transaction rather than its facade or queries.
271
+ - PostgreSQL contracts must have one domain namespace. A model named `transaction` conflicts with the facade's transaction method.
272
+ - SQLite needs a file-backed database; `:memory:` and an empty path are rejected. Its driver uses synchronous database calls. Treat it as a local database path and avoid coordinating separate Layers against the same file.
273
+ - One SQLite Layer serializes finite root query Effects with the entire explicit transaction lifetime. Transaction queries have a separate permit. Root Streams remain incremental; do not treat them as holding that lifetime permit.
274
+ - Transaction Streams buffer the full result. Refine large queries before collecting them in a transaction.
275
+ - The API excludes model variants and direct scalar aggregate shortcuts; use the typed `aggregate` callback. Prisma Next features are not automatically package guarantees.
231
276
 
232
- `effectDB` supports the usual `skip`, `skipIf`, `runIf`, `only`, `each`, and
233
- `fails` variants. Tests install TestClock unless you pass `clock: "live"`.
234
- `withTestTransaction` is also exported for custom harnesses.
277
+ The [roadmap](https://github.com/ShivaeDev/platform/blob/main/packages/effect-prisma/docs/roadmap.md) records package status and maintainer questions. The tests distinguish real PostgreSQL behavior, file-backed SQLite behavior and controlled execution tests; PostgreSQL tests skip when their database URL is absent.
@@ -6,16 +6,16 @@ import type { PrismaError } from "./error.ts";
6
6
  export interface DatabaseLayerOptions extends PostgresOptionsBase {
7
7
  readonly url: string | Redacted.Redacted<string>;
8
8
  }
9
- type DatabaseFactoryOptions<Contract extends AnySqlContract> = {
10
- readonly contract: Contract;
9
+ type DatabaseFactoryOptions<TContract extends AnySqlContract> = {
10
+ readonly contract: TContract;
11
11
  readonly contractJson?: never;
12
12
  } | {
13
13
  readonly contractJson: unknown;
14
14
  readonly contract?: never;
15
15
  };
16
- export interface DatabaseDefinition<Contract extends AnySqlContract, Identifier extends string> extends DatabaseServiceHolder<Contract, Identifier> {
17
- readonly layer: (options: DatabaseLayerOptions) => Layer.Layer<DatabaseIdentifier<Contract, Identifier>, PrismaError>;
16
+ export interface DatabaseDefinition<TContract extends AnySqlContract, TIdentifier extends string> extends DatabaseServiceHolder<TContract, TIdentifier> {
17
+ readonly layer: (options: DatabaseLayerOptions) => Layer.Layer<DatabaseIdentifier<TContract, TIdentifier>, PrismaError>;
18
18
  }
19
- export declare const makeDatabase: <const Contract extends AnySqlContract>() => <const Identifier extends string>(identifier: DatabaseIdentifierLiteral<Identifier>, options: DatabaseFactoryOptions<Contract>) => DatabaseDefinition<Contract, Identifier>;
19
+ export declare const makeDatabase: <const TContract extends AnySqlContract>() => <const TIdentifier extends string>(identifier: DatabaseIdentifierLiteral<TIdentifier>, options: DatabaseFactoryOptions<TContract>) => DatabaseDefinition<TContract, TIdentifier>;
20
20
  export {};
21
21
  //# sourceMappingURL=database.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA,OAAiB,EAAuB,KAAK,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACxG,OAAO,EAAE,KAAK,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,qBAAqB,EAAiB,MAAM,mBAAmB,CAAC;AAI7H,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB;IAChE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACjD;AAED,KAAK,sBAAsB,CAAC,QAAQ,SAAS,cAAc,IACxD;IACA,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC;CAC7B,GACD;IACA,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;CACzB,CAAC;AAEL,MAAM,WAAW,kBAAkB,CAAC,QAAQ,SAAS,cAAc,EAAE,UAAU,SAAS,MAAM,CAAE,SAAQ,qBAAqB,CAAC,QAAQ,EAAE,UAAU,CAAC;IAClJ,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;CACtH;AAaD,eAAO,MAAM,YAAY,GACvB,KAAK,CAAC,QAAQ,SAAS,cAAc,QACrC,KAAK,CAAC,UAAU,SAAS,MAAM,EAC/B,YAAY,yBAAyB,CAAC,UAAU,CAAC,EACjD,SAAS,sBAAsB,CAAC,QAAQ,CAAC,KACvC,kBAAkB,CAAC,QAAQ,EAAE,UAAU,CAgCzC,CAAC"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA,OAAiB,EAAuB,KAAK,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACxG,OAAO,EAAE,KAAK,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,qBAAqB,EAAiB,MAAM,mBAAmB,CAAC;AAI7H,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB;IAChE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACjD;AAED,KAAK,sBAAsB,CAAC,SAAS,SAAS,cAAc,IACzD;IACA,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC;CAC7B,GACD;IACA,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;CACzB,CAAC;AAEL,MAAM,WAAW,kBAAkB,CAAC,SAAS,SAAS,cAAc,EAAE,WAAW,SAAS,MAAM,CAC/F,SAAQ,qBAAqB,CAAC,SAAS,EAAE,WAAW,CAAC;IACrD,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC;CACxH;AAkBD,eAAO,MAAM,YAAY,GACvB,KAAK,CAAC,SAAS,SAAS,cAAc,QACtC,KAAK,CAAC,WAAW,SAAS,MAAM,cACpB,yBAAyB,CAAC,WAAW,CAAC,WACzC,sBAAsB,CAAC,SAAS,CAAC,KACxC,kBAAkB,CAAC,SAAS,EAAE,WAAW,CAgC3C,CAAC"}
package/dist/database.js CHANGED
@@ -4,14 +4,18 @@ import { acquireConnectedClient } from "#internal/client-lifecycle.ts";
4
4
  import { namespaceModels } from "#internal/database-facade.ts";
5
5
  import { makeSqlDatabase } from "#internal/database-factory.ts";
6
6
  import { fromPrismaPromise } from "#internal/promise.ts";
7
- const clientOptions = ({ url, extensions, middleware, poolOptions, verifyMarker }) => ({
8
- url: typeof url === "string" ? url : Redacted.value(url),
9
- ...(extensions === undefined ? {} : { extensions }),
10
- ...(middleware === undefined ? {} : { middleware }),
11
- ...(poolOptions === undefined ? {} : { poolOptions }),
12
- ...(verifyMarker === undefined ? {} : { verifyMarker }),
13
- });
14
- const defaultModels = (client) => namespaceModels(client.contract, client.orm);
7
+ function clientOptions({ url, extensions, middleware, poolOptions, verifyMarker }) {
8
+ return {
9
+ url: typeof url === "string" ? url : Redacted.value(url),
10
+ ...(extensions === undefined ? {} : { extensions }),
11
+ ...(middleware === undefined ? {} : { middleware }),
12
+ ...(poolOptions === undefined ? {} : { poolOptions }),
13
+ ...(verifyMarker === undefined ? {} : { verifyMarker }),
14
+ };
15
+ }
16
+ function defaultModels(client) {
17
+ return namespaceModels(client.contract, client.orm);
18
+ }
15
19
  export const makeDatabase = () => (identifier, options) => {
16
20
  return makeSqlDatabase(identifier, (layerOptions) => {
17
21
  const forwarded = clientOptions(layerOptions);
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAiD,MAAM,+BAA+B,CAAC;AACxG,OAAO,EAAc,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAqBzD,MAAM,aAAa,GAAG,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAwB,EAAE,EAAE,CAAC,CAAC;IAC5G,GAAG,EAAE,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC;IACxD,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;IACnD,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;IACnD,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IACrD,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;CACvD,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAyD,MAA0D,EAAU,EAAE,CACpJ,eAAe,CAAmB,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;AAEhE,MAAM,CAAC,MAAM,YAAY,GACxB,GAA0C,EAAE,CAC5C,CACC,UAAiD,EACjD,OAAyC,EACE,EAAE;IAG7C,OAAO,eAAe,CAA6C,UAAU,EAAE,CAAC,YAAY,EAAE,EAAE;QAC/F,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;QAC9C,MAAM,MAAM,GACX,OAAO,CAAC,QAAQ,KAAK,SAAS;YAC7B,CAAC,CAAC,QAAQ,CAAW;gBACnB,GAAG,SAAS;gBACZ,YAAY,EAAE,OAAO,CAAC,YAAY;aAClC,CAAC;YACH,CAAC,CAAC,QAAQ,CAAW;gBACnB,GAAG,SAAS;gBACZ,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC1B,CAAC,CAAC;QAEN,OAAO,iBAAiB,CAAC,GAAG,EAAE,CAC7B,sBAAsB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACrC,MAAM;YACN,QAAQ,EAAE,EAAE;YACZ,QAAQ,EAAE;gBACT,UAAU,EAAE,yBAAyB;gBACrC,IAAI,EAAE,IAAI;aACV;YACD,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,aAAa,CAAmB,MAAM,CAAC;YAC/C,cAAc,EAAE,SAAS;YACzB,mBAAmB,EAAE,SAAS;YAC9B,oBAAoB,EAAE,SAAS;SAC/B,CAAC,CAAC,CACH,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAiD,MAAM,+BAA+B,CAAC;AACxG,OAAO,EAAc,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAsBzD,SAAS,aAAa,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAwB;IACtG,OAAO;QACN,GAAG,EAAE,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC;QACxD,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;QACnD,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;QACnD,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACrD,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;KACvD,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CACrB,MAA2D;IAE3D,OAAO,eAAe,CAAqB,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GACxB,GAA2C,EAAE,CAC7C,CACC,UAAkD,EAClD,OAA0C,EACG,EAAE;IAG/C,OAAO,eAAe,CAA+C,UAAU,EAAE,CAAC,YAAY,EAAE,EAAE;QACjG,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;QAC9C,MAAM,MAAM,GACX,OAAO,CAAC,QAAQ,KAAK,SAAS;YAC7B,CAAC,CAAC,QAAQ,CAAY;gBACpB,GAAG,SAAS;gBACZ,YAAY,EAAE,OAAO,CAAC,YAAY;aAClC,CAAC;YACH,CAAC,CAAC,QAAQ,CAAY;gBACpB,GAAG,SAAS;gBACZ,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC1B,CAAC,CAAC;QAEN,OAAO,iBAAiB,CAAC,GAAG,EAAE,CAC7B,sBAAsB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACrC,MAAM;YACN,QAAQ,EAAE,EAAE;YACZ,QAAQ,EAAE;gBACT,UAAU,EAAE,yBAAyB;gBACrC,IAAI,EAAE,IAAI;aACV;YACD,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,aAAa,CAAoB,MAAM,CAAC;YAChD,cAAc,EAAE,SAAS;YACzB,mBAAmB,EAAE,SAAS;YAC9B,oBAAoB,EAAE,SAAS;SAC/B,CAAC,CAAC,CACH,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC"}
@@ -3,31 +3,31 @@ import type { Context, Effect, Layer } from "effect";
3
3
  import type { PrismaError } from "#error.ts";
4
4
  import type { AnySqlContract } from "#internal/executor.ts";
5
5
  import type { Relation } from "#relation.ts";
6
- type OrmFor<Contract extends AnySqlContract> = ReturnType<typeof orm<Contract>>;
7
- type DefaultNamespaceId<Contract extends AnySqlContract> = "__unbound__" extends keyof OrmFor<Contract> ? "__unbound__" : "public" extends keyof OrmFor<Contract> ? "public" : keyof OrmFor<Contract>;
8
- export type DefaultModels<Contract extends AnySqlContract> = OrmFor<Contract>[DefaultNamespaceId<Contract>] extends infer Models extends object ? Models : never;
9
- type IsUnion<Value, Whole = Value> = Value extends unknown ? ([Whole] extends [Value] ? false : true) : never;
10
- type IsSingletonString<Value extends string> = Record<never, never> extends Record<Value, never> ? false : true;
6
+ type OrmFor<TContract extends AnySqlContract> = ReturnType<typeof orm<TContract>>;
7
+ type DefaultNamespaceId<TContract extends AnySqlContract> = "__unbound__" extends keyof OrmFor<TContract> ? "__unbound__" : "public" extends keyof OrmFor<TContract> ? "public" : keyof OrmFor<TContract>;
8
+ export type DefaultModels<TContract extends AnySqlContract> = OrmFor<TContract>[DefaultNamespaceId<TContract>] extends infer TModels extends object ? TModels : never;
9
+ type IsUnion<TValue, TWhole = TValue> = TValue extends unknown ? ([TWhole] extends [TValue] ? false : true) : never;
10
+ type IsSingletonString<TValue extends string> = Record<never, never> extends Record<TValue, never> ? false : true;
11
11
  export declare const internalContextIdentifierPrefix = "\0@shivaedev/effect-prisma/internal/";
12
- export type DatabaseIdentifierLiteral<Identifier extends string> = [Identifier] extends [`\0@shivaedev/effect-prisma/internal/${string}`] ? never : string extends Identifier ? never : true extends IsUnion<Identifier> ? never : IsSingletonString<Identifier> extends true ? Identifier : never;
13
- export interface DatabaseIdentifier<Contract extends AnySqlContract, Identifier extends string> {
14
- readonly _contract: (contract: Contract) => Contract;
15
- readonly _databaseIdentifier: (identifier: Identifier) => Identifier;
12
+ export type DatabaseIdentifierLiteral<TIdentifier extends string> = [TIdentifier] extends [`\0@shivaedev/effect-prisma/internal/${string}`] ? never : string extends TIdentifier ? never : true extends IsUnion<TIdentifier> ? never : IsSingletonString<TIdentifier> extends true ? TIdentifier : never;
13
+ export interface DatabaseIdentifier<TContract extends AnySqlContract, TIdentifier extends string> {
14
+ readonly _contract: (contract: TContract) => TContract;
15
+ readonly _databaseIdentifier: (identifier: TIdentifier) => TIdentifier;
16
16
  }
17
- type DatabaseModels<Contract extends AnySqlContract, Identifier extends string> = {
18
- readonly [Model in keyof DefaultModels<Contract>]: Relation<DefaultModels<Contract>[Model], Contract, Model & string, DatabaseIdentifier<Contract, Identifier>>;
17
+ type DatabaseModels<TContract extends AnySqlContract, TIdentifier extends string> = {
18
+ readonly [TModel in keyof DefaultModels<TContract>]: Relation<DefaultModels<TContract>[TModel], TContract, TModel & string, DatabaseIdentifier<TContract, TIdentifier>>;
19
19
  };
20
- export type DatabaseService<Contract extends AnySqlContract, Identifier extends string> = DatabaseModels<Contract, Identifier> & {
21
- transaction: <A, E, R>(program: Effect.Effect<A, E, R> & (DatabaseIdentifier<Contract, Identifier> extends R ? unknown : never)) => Effect.Effect<A, E | PrismaError, Exclude<R, DatabaseIdentifier<Contract, Identifier>>>;
20
+ export type DatabaseService<TContract extends AnySqlContract, TIdentifier extends string> = DatabaseModels<TContract, TIdentifier> & {
21
+ transaction: <A, E, R>(program: Effect.Effect<A, E, R> & (DatabaseIdentifier<TContract, TIdentifier> extends R ? unknown : never)) => Effect.Effect<A, E | PrismaError, Exclude<R, DatabaseIdentifier<TContract, TIdentifier>>>;
22
22
  };
23
- export interface DatabaseServiceHolder<Contract extends AnySqlContract, Identifier extends string> extends Context.Service<DatabaseIdentifier<Contract, Identifier>, DatabaseService<Contract, Identifier>> {
23
+ export interface DatabaseServiceHolder<TContract extends AnySqlContract, TIdentifier extends string> extends Context.Service<DatabaseIdentifier<TContract, TIdentifier>, DatabaseService<TContract, TIdentifier>> {
24
24
  }
25
25
  export type AnyDatabase = Effect.Effect<unknown, never, unknown> & {
26
26
  readonly layer: (...arguments_: readonly never[]) => Layer.Any;
27
27
  };
28
- export type DatabaseServiceOf<Database extends AnyDatabase> = Effect.Success<Database>;
29
- export interface SqlDatabase<Contract extends AnySqlContract, Identifier extends string, Options> extends DatabaseServiceHolder<Contract, Identifier> {
30
- readonly layer: (options: Options) => Layer.Layer<DatabaseIdentifier<Contract, Identifier>, PrismaError>;
28
+ export type DatabaseServiceOf<TDatabase extends AnyDatabase> = Effect.Success<TDatabase>;
29
+ export interface SqlDatabase<TContract extends AnySqlContract, TIdentifier extends string, TOptions> extends DatabaseServiceHolder<TContract, TIdentifier> {
30
+ readonly layer: (options: TOptions) => Layer.Layer<DatabaseIdentifier<TContract, TIdentifier>, PrismaError>;
31
31
  }
32
32
  export {};
33
33
  //# sourceMappingURL=databaseTypes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"databaseTypes.d.ts","sourceRoot":"","sources":["../src/databaseTypes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"databaseTypes.d.ts","sourceRoot":"","sources":["../src/databaseTypes.ts"],"names":[],"mappings":"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"}