@shivaedev/effect-prisma 0.5.2 → 0.6.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 (87) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +25 -7
  3. package/dist/database.d.ts +6 -6
  4. package/dist/database.d.ts.map +1 -1
  5. package/dist/database.js +9 -2
  6. package/dist/database.js.map +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/internal/database-factory.d.ts +20 -22
  11. package/dist/internal/database-factory.d.ts.map +1 -1
  12. package/dist/internal/database-factory.js +83 -24
  13. package/dist/internal/database-factory.js.map +1 -1
  14. package/dist/internal/executor.d.ts +9 -7
  15. package/dist/internal/executor.d.ts.map +1 -1
  16. package/dist/internal/query-execution.d.ts +3 -4
  17. package/dist/internal/query-execution.d.ts.map +1 -1
  18. package/dist/internal/query-execution.js +9 -3
  19. package/dist/internal/query-execution.js.map +1 -1
  20. package/dist/internal/recipe.d.ts +1 -1
  21. package/dist/internal/recipe.d.ts.map +1 -1
  22. package/dist/internal/recipe.js +22 -9
  23. package/dist/internal/recipe.js.map +1 -1
  24. package/dist/internal/relation-plan.d.ts +6 -1
  25. package/dist/internal/relation-plan.d.ts.map +1 -1
  26. package/dist/internal/relation-plan.js +6 -5
  27. package/dist/internal/relation-plan.js.map +1 -1
  28. package/dist/internal/relation-runtime.d.ts +4 -3
  29. package/dist/internal/relation-runtime.d.ts.map +1 -1
  30. package/dist/internal/relation-runtime.js +38 -19
  31. package/dist/internal/relation-runtime.js.map +1 -1
  32. package/dist/internal/relation-stream.d.ts +3 -4
  33. package/dist/internal/relation-stream.d.ts.map +1 -1
  34. package/dist/internal/relation-stream.js +23 -21
  35. package/dist/internal/relation-stream.js.map +1 -1
  36. package/dist/internal/testing.d.ts +5 -5
  37. package/dist/internal/testing.d.ts.map +1 -1
  38. package/dist/internal/testing.js.map +1 -1
  39. package/dist/internal/transaction.d.ts +6 -3
  40. package/dist/internal/transaction.d.ts.map +1 -1
  41. package/dist/internal/transaction.js +14 -4
  42. package/dist/internal/transaction.js.map +1 -1
  43. package/dist/internal/vitest-database.d.ts +2 -2
  44. package/dist/internal/vitest-database.d.ts.map +1 -1
  45. package/dist/internal/vitest-database.js.map +1 -1
  46. package/dist/relation/include.d.ts +10 -8
  47. package/dist/relation/include.d.ts.map +1 -1
  48. package/dist/relation/prisma-methods.d.ts +20 -20
  49. package/dist/relation/prisma-methods.d.ts.map +1 -1
  50. package/dist/relation.d.ts +8 -7
  51. package/dist/relation.d.ts.map +1 -1
  52. package/dist/sqlite.d.ts +6 -6
  53. package/dist/sqlite.d.ts.map +1 -1
  54. package/dist/sqlite.js +10 -2
  55. package/dist/sqlite.js.map +1 -1
  56. package/dist/testing/transaction.d.ts +3 -3
  57. package/dist/testing/transaction.d.ts.map +1 -1
  58. package/dist/testing/transaction.js.map +1 -1
  59. package/dist/testing/types.d.ts +1 -1
  60. package/dist/testing/types.d.ts.map +1 -1
  61. package/dist/testing/vitest.d.ts +3 -3
  62. package/dist/testing/vitest.d.ts.map +1 -1
  63. package/dist/testing/vitest.js.map +1 -1
  64. package/dist/testing.d.ts +1 -1
  65. package/dist/testing.d.ts.map +1 -1
  66. package/dist/testing.js.map +1 -1
  67. package/package.json +5 -5
  68. package/src/database.ts +53 -47
  69. package/src/index.ts +0 -1
  70. package/src/internal/database-factory.ts +221 -98
  71. package/src/internal/executor.ts +9 -15
  72. package/src/internal/query-execution.ts +14 -6
  73. package/src/internal/recipe.ts +46 -7
  74. package/src/internal/relation-plan.ts +11 -11
  75. package/src/internal/relation-runtime.ts +67 -65
  76. package/src/internal/relation-stream.ts +54 -48
  77. package/src/internal/testing.ts +8 -8
  78. package/src/internal/transaction.ts +80 -56
  79. package/src/internal/vitest-database.ts +3 -12
  80. package/src/relation/include.ts +31 -22
  81. package/src/relation/prisma-methods.ts +27 -31
  82. package/src/relation.ts +19 -27
  83. package/src/sqlite.ts +61 -54
  84. package/src/testing/transaction.ts +16 -6
  85. package/src/testing/types.ts +1 -1
  86. package/src/testing/vitest.ts +3 -10
  87. package/src/testing.ts +0 -1
@@ -1,12 +1,7 @@
1
- import type { Context } from "effect";
2
1
  import { Effect, Effectable, Option, type Stream } from "effect";
3
2
  import type { PrismaError } from "../error.js";
4
3
  import type { Relation } from "../relation.js";
5
- import type {
6
- AnyPostgresContract,
7
- DatabaseExecutor,
8
- ExecutorIdentifier,
9
- } from "./executor.js";
4
+ import type { AnyPostgresContract, DatabaseExecutor } from "./executor.js";
10
5
  import { fromPrismaPromise } from "./promise.js";
11
6
  import { executeQuery } from "./query-execution.js";
12
7
  import {
@@ -15,7 +10,7 @@ import {
15
10
  replayRecipe,
16
11
  rootRecipe,
17
12
  } from "./recipe.js";
18
- import { RelationPlanTypeId } from "./relation-plan.js";
13
+ import { setRelationPlan } from "./relation-plan.js";
19
14
  import { makeRelationStream } from "./relation-stream.js";
20
15
 
21
16
  type AnyFunction = (...arguments_: ReadonlyArray<never>) => unknown;
@@ -24,11 +19,12 @@ interface RelationRuntime<
24
19
  Models extends object,
25
20
  Contract extends AnyPostgresContract,
26
21
  > {
27
- readonly executor: Context.Service<
28
- ExecutorIdentifier<Models>,
29
- DatabaseExecutor<Models, Contract>
30
- >;
22
+ readonly executor: DatabaseExecutor<Models, Contract>;
31
23
  readonly recipe: RelationRecipe;
24
+ readonly resolveExecutor: Effect.Effect<
25
+ DatabaseExecutor<Models, Contract>,
26
+ PrismaError
27
+ >;
32
28
  readonly terminal?: PropertyKey;
33
29
  }
34
30
 
@@ -90,74 +86,72 @@ const evaluateResult = (
90
86
  return Effect.succeed(executable);
91
87
  };
92
88
 
93
- interface RelationValue<
94
- Models extends object,
95
- Contract extends AnyPostgresContract,
96
- > extends Effect.Effect<unknown, PrismaError, ExecutorIdentifier<Models>> {
97
- readonly [RelationPlanTypeId]: {
98
- readonly recipe: RelationRecipe;
99
- readonly terminal?: PropertyKey;
100
- };
101
- readonly runtime: RelationRuntime<Models, Contract>;
102
- readonly stream: Stream.Stream<
103
- unknown,
104
- PrismaError,
105
- ExecutorIdentifier<Models>
106
- >;
89
+ interface RelationValue extends Effect.Effect<unknown, PrismaError> {
90
+ readonly stream: Stream.Stream<unknown, PrismaError>;
107
91
  }
108
92
 
93
+ const runtimes = new WeakMap<object, unknown>();
94
+
95
+ const runtimeOf = <Models extends object, Contract extends AnyPostgresContract>(
96
+ self: object,
97
+ ): RelationRuntime<Models, Contract> => {
98
+ const runtime = runtimes.get(self);
99
+ if (runtime === undefined) {
100
+ throw new TypeError("Relation runtime is unavailable");
101
+ }
102
+ return runtime as RelationRuntime<Models, Contract>;
103
+ };
104
+
109
105
  const relationEffect = <
110
106
  Models extends object,
111
107
  Contract extends AnyPostgresContract,
112
108
  >(
113
- self: RelationValue<Models, Contract>,
114
- ): Effect.Effect<unknown, PrismaError, ExecutorIdentifier<Models>> =>
115
- Effect.flatMap(self.runtime.executor, (executor) =>
109
+ self: RelationValue,
110
+ ): Effect.Effect<unknown, PrismaError> => {
111
+ const runtime = runtimeOf<Models, Contract>(self);
112
+ return Effect.flatMap(runtime.resolveExecutor, (executor) =>
116
113
  executeQuery(
117
114
  executor,
118
115
  Effect.suspend(() =>
119
116
  evaluateResult(
120
- replayRecipe(executor.models, self.runtime.recipe),
121
- self.runtime.terminal,
117
+ replayRecipe(
118
+ executor.models,
119
+ runtime.recipe,
120
+ executor.identity,
121
+ executor.transactionIdentity,
122
+ ),
123
+ runtime.terminal,
122
124
  ),
123
125
  ),
124
126
  ),
125
127
  ).pipe(
126
128
  Effect.withSpan(
127
- `prisma.${self.runtime.recipe.model}.${String(self.runtime.terminal ?? "all")}`,
129
+ `prisma.${runtime.recipe.model}.${String(runtime.terminal ?? "all")}`,
128
130
  {
129
131
  kind: "client",
130
132
  attributes: {
131
133
  "db.system": "postgresql",
132
- "db.model": self.runtime.recipe.model,
133
- "db.operation": String(self.runtime.terminal ?? "all"),
134
+ "db.model": runtime.recipe.model,
135
+ "db.operation": String(runtime.terminal ?? "all"),
134
136
  },
135
137
  },
136
138
  ),
137
139
  );
140
+ };
138
141
 
139
142
  const RelationPrototype = {
140
- ...Effectable.Prototype<
141
- RelationValue<Record<string, unknown>, AnyPostgresContract>
142
- >({
143
+ ...Effectable.Prototype<RelationValue>({
143
144
  label: "EffectPrismaRelation",
144
145
  evaluate() {
145
146
  return relationEffect(this);
146
147
  },
147
148
  }),
148
- get stream(): Stream.Stream<
149
- unknown,
150
- PrismaError,
151
- ExecutorIdentifier<Record<string, unknown>>
152
- > {
153
- const relation = this as RelationValue<
154
- Record<string, unknown>,
155
- AnyPostgresContract
156
- >;
157
- return makeRelationStream(
158
- relation.runtime.executor,
159
- relation.runtime.recipe,
149
+ get stream(): Stream.Stream<unknown, PrismaError> {
150
+ const relation = this as RelationValue;
151
+ const runtime = runtimeOf<Record<string, unknown>, AnyPostgresContract>(
152
+ relation,
160
153
  );
154
+ return makeRelationStream(runtime.resolveExecutor, runtime.recipe);
161
155
  },
162
156
  };
163
157
 
@@ -167,15 +161,8 @@ const makeRelationProxy = <
167
161
  >(
168
162
  runtime: RelationRuntime<Models, Contract>,
169
163
  ): unknown => {
170
- const target = Object.assign(Object.create(RelationPrototype), {
171
- [RelationPlanTypeId]: {
172
- recipe: runtime.recipe,
173
- ...(runtime.terminal === undefined ? {} : { terminal: runtime.terminal }),
174
- },
175
- runtime,
176
- }) as RelationValue<Models, Contract>;
177
-
178
- return new Proxy(target, {
164
+ const target = Object.create(RelationPrototype) as RelationValue;
165
+ const proxy = new Proxy(target, {
179
166
  get(self, property, receiver) {
180
167
  if (property === "then") {
181
168
  return undefined;
@@ -205,20 +192,35 @@ const makeRelationProxy = <
205
192
  });
206
193
  },
207
194
  });
195
+ const plan = {
196
+ liveness: runtime.executor.liveness,
197
+ owner: runtime.executor.identity,
198
+ recipe: runtime.recipe,
199
+ ...(runtime.terminal === undefined ? {} : { terminal: runtime.terminal }),
200
+ transactionIdentity: runtime.executor.transactionIdentity,
201
+ };
202
+ for (const value of [target, proxy]) {
203
+ runtimes.set(value, runtime);
204
+ setRelationPlan(value, plan);
205
+ }
206
+ return proxy;
208
207
  };
209
208
 
210
209
  export const makeModelRelation = <
211
210
  Collection,
212
211
  Models extends object,
213
- Contract extends AnyPostgresContract = AnyPostgresContract,
212
+ ExecutorContract extends AnyPostgresContract = AnyPostgresContract,
213
+ Model extends string = string,
214
214
  >(
215
- executor: Context.Service<
216
- ExecutorIdentifier<Models>,
217
- DatabaseExecutor<Models, Contract>
218
- >,
219
- model: string,
220
- ): Relation<Collection, ExecutorIdentifier<Models>> =>
215
+ executor: DatabaseExecutor<Models, ExecutorContract>,
216
+ model: Model,
217
+ resolveExecutor: Effect.Effect<
218
+ DatabaseExecutor<Models, ExecutorContract>,
219
+ PrismaError
220
+ > = Effect.succeed(executor),
221
+ ): Relation<Collection, undefined, Model> =>
221
222
  makeRelationProxy({
222
223
  executor,
223
224
  recipe: rootRecipe(model),
224
- }) as Relation<Collection, ExecutorIdentifier<Models>>;
225
+ resolveExecutor,
226
+ }) as Relation<Collection, undefined, Model>;
@@ -1,11 +1,6 @@
1
- import type { Context } from "effect";
2
1
  import { Effect, Stream } from "effect";
3
2
  import { isPrismaFailure, type PrismaError, toPrismaError } from "../error.js";
4
- import type {
5
- AnyPostgresContract,
6
- DatabaseExecutor,
7
- ExecutorIdentifier,
8
- } from "./executor.js";
3
+ import type { AnyPostgresContract, DatabaseExecutor } from "./executor.js";
9
4
  import { fromPrismaPromise } from "./promise.js";
10
5
  import { executeQuery } from "./query-execution.js";
11
6
  import { type RelationRecipe, replayRecipe } from "./recipe.js";
@@ -16,51 +11,62 @@ export const makeRelationStream = <
16
11
  Models extends object,
17
12
  Contract extends AnyPostgresContract,
18
13
  >(
19
- executorService: Context.Service<
20
- ExecutorIdentifier<Models>,
21
- DatabaseExecutor<Models, Contract>
14
+ resolveExecutor: Effect.Effect<
15
+ DatabaseExecutor<Models, Contract>,
16
+ PrismaError
22
17
  >,
23
18
  recipe: RelationRecipe,
24
- ): Stream.Stream<unknown, PrismaError, ExecutorIdentifier<Models>> =>
25
- Stream.unwrap(
26
- Effect.map(executorService, (executor) => {
27
- const collection = replayRecipe(executor.models, recipe);
28
- if (
29
- typeof collection !== "object" ||
30
- collection === null ||
31
- typeof Reflect.get(collection, "all") !== "function"
32
- ) {
33
- throw new TypeError("Only collection Relations can be streamed");
34
- }
35
- const iterable = Reflect.apply(
36
- Reflect.get(collection, "all") as AnyFunction,
37
- collection,
38
- [],
39
- ) as AsyncIterable<unknown>;
40
-
41
- if (executor.querySemaphore === undefined) {
42
- return Stream.fromAsyncIterable(iterable, (error) => error).pipe(
43
- Stream.catch((error) =>
44
- isPrismaFailure(error)
45
- ? Stream.fail(toPrismaError(error))
46
- : Stream.die(error),
47
- ),
48
- );
49
- }
50
-
51
- const buffered = executeQuery(
19
+ ): Stream.Stream<unknown, PrismaError> => {
20
+ return Stream.unwrap(
21
+ Effect.flatMap(resolveExecutor, (executor) =>
22
+ executeQuery(
52
23
  executor,
53
- fromPrismaPromise(async () => {
54
- const rows: Array<unknown> = [];
55
- for await (const row of iterable) {
56
- rows.push(row);
24
+ Effect.sync(() => {
25
+ const collection = replayRecipe(
26
+ executor.models,
27
+ recipe,
28
+ executor.identity,
29
+ executor.transactionIdentity,
30
+ );
31
+ if (
32
+ typeof collection !== "object" ||
33
+ collection === null ||
34
+ typeof Reflect.get(collection, "all") !== "function"
35
+ ) {
36
+ throw new TypeError("Only collection Relations can be streamed");
57
37
  }
58
- return rows;
59
- }),
60
- );
38
+ const iterable = Reflect.apply(
39
+ Reflect.get(collection, "all") as AnyFunction,
40
+ collection,
41
+ [],
42
+ ) as AsyncIterable<unknown>;
61
43
 
62
- return Stream.unwrap(
63
- Effect.map(buffered, (rows) => Stream.fromIterable(rows)),
64
- );
65
- }),
44
+ if (executor.querySemaphore === undefined) {
45
+ return Stream.fromAsyncIterable(iterable, (error) => error).pipe(
46
+ Stream.catch((error) =>
47
+ isPrismaFailure(error)
48
+ ? Stream.fail(toPrismaError(error))
49
+ : Stream.die(error),
50
+ ),
51
+ );
52
+ }
53
+
54
+ const buffered = executeQuery(
55
+ executor,
56
+ fromPrismaPromise(async () => {
57
+ const rows: Array<unknown> = [];
58
+ for await (const row of iterable) {
59
+ rows.push(row);
60
+ }
61
+ return rows;
62
+ }),
63
+ );
64
+
65
+ return Stream.unwrap(
66
+ Effect.map(buffered, (rows) => Stream.fromIterable(rows)),
67
+ );
68
+ }),
69
+ ),
70
+ ),
66
71
  );
72
+ };
@@ -5,21 +5,21 @@ export const DatabaseTestingTypeId: unique symbol = Symbol.for(
5
5
  "@shivaedev/effect-prisma/DatabaseTesting",
6
6
  );
7
7
 
8
- export interface DatabaseTesting<Requirement> {
8
+ export interface DatabaseTesting<DatabaseId> {
9
9
  readonly withTestTransaction: <A, E, R>(
10
- program: Effect.Effect<A, E, R>,
11
- ) => Effect.Effect<A, E | PrismaError, R | Requirement>;
10
+ program: Effect.Effect<A, E, R> & (DatabaseId extends R ? unknown : never),
11
+ ) => Effect.Effect<A, E | PrismaError, DatabaseId | Exclude<R, DatabaseId>>;
12
12
  }
13
13
 
14
- export interface DatabaseWithTesting<Requirement> {
15
- readonly [DatabaseTestingTypeId]: DatabaseTesting<Requirement>;
14
+ export interface DatabaseWithTesting<DatabaseId> {
15
+ readonly [DatabaseTestingTypeId]: DatabaseTesting<DatabaseId>;
16
16
  }
17
17
 
18
- export const getDatabaseTesting = <Requirement>(
18
+ export const getDatabaseTesting = <DatabaseId>(
19
19
  database: object,
20
- ): DatabaseTesting<Requirement> => {
20
+ ): DatabaseTesting<DatabaseId> => {
21
21
  const testing = Reflect.get(database, DatabaseTestingTypeId) as
22
- | DatabaseTesting<Requirement>
22
+ | DatabaseTesting<DatabaseId>
23
23
  | undefined;
24
24
 
25
25
  if (testing === undefined) {
@@ -12,13 +12,21 @@ export type TransactionOrm<Contract extends AnySqlContract> = ReturnType<
12
12
  typeof orm<Contract>
13
13
  >;
14
14
 
15
+ export const withTransactionSemaphore = <A, E, R>(
16
+ semaphore: Semaphore.Semaphore | undefined,
17
+ transaction: Effect.Effect<A, E, R>,
18
+ ): Effect.Effect<A, E, R> =>
19
+ semaphore === undefined ? transaction : semaphore.withPermit(transaction);
20
+
15
21
  export interface TransactionResource<
16
22
  Models extends object,
17
23
  Contract extends AnySqlContract,
18
24
  > {
19
25
  readonly connection: RuntimeConnection;
20
26
  readonly transaction: RuntimeTransaction;
21
- readonly executor: DatabaseExecutor<Models, Contract>;
27
+ readonly executor: DatabaseExecutor<Models, Contract> & {
28
+ readonly querySemaphore: Semaphore.Semaphore;
29
+ };
22
30
  }
23
31
 
24
32
  const runtimeFailure = (
@@ -34,6 +42,7 @@ export const acquireTransaction = <
34
42
  >(
35
43
  current: DatabaseExecutor<Models, Contract>,
36
44
  models: (orm: TransactionOrm<Contract>) => Models,
45
+ mode: "test" | "transaction",
37
46
  ): Effect.Effect<TransactionResource<Models, Contract>, PrismaError> =>
38
47
  fromPrismaPromise(async () => {
39
48
  const connection = await current.client.runtime().connection();
@@ -50,9 +59,16 @@ export const acquireTransaction = <
50
59
  transaction,
51
60
  executor: {
52
61
  client: current.client,
62
+ identity: current.identity,
63
+ liveness: {
64
+ closedCode: "RUNTIME.TRANSACTION_CLOSED",
65
+ open: true,
66
+ },
67
+ mode,
53
68
  models: models(transactionOrm),
54
69
  querySemaphore: Semaphore.makeUnsafe(1),
55
- transactional: true,
70
+ transactionIdentity: {},
71
+ transactionSemaphore: current.transactionSemaphore,
56
72
  },
57
73
  };
58
74
  } catch (error) {
@@ -92,62 +108,70 @@ const settleTransaction = <
92
108
  commitOnSuccess: boolean,
93
109
  ): Effect.Effect<void, PrismaError> =>
94
110
  Effect.uninterruptible(
95
- fromPrismaPromise(async () => {
96
- let disposed = false;
97
- let failure: unknown;
111
+ Effect.sync(() => {
112
+ resource.executor.liveness.open = false;
113
+ }).pipe(
114
+ Effect.andThen(
115
+ resource.executor.querySemaphore.withPermit(
116
+ fromPrismaPromise(async () => {
117
+ let disposed = false;
118
+ let failure: unknown;
98
119
 
99
- const destroy = async (reason: unknown): Promise<void> => {
100
- if (disposed) {
101
- return;
102
- }
103
- disposed = true;
104
- await resource.connection.destroy(reason).catch(() => undefined);
105
- };
120
+ const destroy = async (reason: unknown): Promise<void> => {
121
+ if (disposed) {
122
+ return;
123
+ }
124
+ disposed = true;
125
+ await resource.connection.destroy(reason).catch(() => undefined);
126
+ };
106
127
 
107
- if (commitOnSuccess && Exit.isSuccess(exit)) {
108
- try {
109
- await resource.transaction.commit();
110
- } catch (commitError) {
111
- try {
112
- await resource.transaction.rollback();
113
- } catch {
114
- await destroy(commitError);
115
- }
116
- failure = runtimeFailure(
117
- "RUNTIME.TRANSACTION_COMMIT_FAILED",
118
- commitError,
119
- );
120
- }
121
- } else {
122
- try {
123
- await resource.transaction.rollback();
124
- } catch (rollbackError) {
125
- await destroy(rollbackError);
126
- failure = runtimeFailure(
127
- "RUNTIME.TRANSACTION_ROLLBACK_FAILED",
128
- rollbackError,
129
- );
130
- }
131
- }
128
+ if (commitOnSuccess && Exit.isSuccess(exit)) {
129
+ try {
130
+ await resource.transaction.commit();
131
+ } catch (commitError) {
132
+ try {
133
+ await resource.transaction.rollback();
134
+ } catch {
135
+ await destroy(commitError);
136
+ }
137
+ failure = runtimeFailure(
138
+ "RUNTIME.TRANSACTION_COMMIT_FAILED",
139
+ commitError,
140
+ );
141
+ }
142
+ } else {
143
+ try {
144
+ await resource.transaction.rollback();
145
+ } catch (rollbackError) {
146
+ await destroy(rollbackError);
147
+ failure = runtimeFailure(
148
+ "RUNTIME.TRANSACTION_ROLLBACK_FAILED",
149
+ rollbackError,
150
+ );
151
+ }
152
+ }
132
153
 
133
- if (!disposed) {
134
- try {
135
- await resource.connection.release();
136
- } catch (releaseError) {
137
- await destroy(releaseError);
138
- if (failure !== undefined) {
139
- throw runtimeFailure(
140
- "RUNTIME.TRANSACTION_RELEASE_FAILED",
141
- failure,
142
- { releaseError },
143
- );
144
- }
145
- throw releaseError;
146
- }
147
- }
154
+ if (!disposed) {
155
+ try {
156
+ await resource.connection.release();
157
+ } catch (releaseError) {
158
+ await destroy(releaseError);
159
+ if (failure !== undefined) {
160
+ throw runtimeFailure(
161
+ "RUNTIME.TRANSACTION_RELEASE_FAILED",
162
+ failure,
163
+ { releaseError },
164
+ );
165
+ }
166
+ throw releaseError;
167
+ }
168
+ }
148
169
 
149
- if (failure !== undefined) {
150
- throw failure;
151
- }
152
- }),
170
+ if (failure !== undefined) {
171
+ throw failure;
172
+ }
173
+ }),
174
+ ),
175
+ ),
176
+ ),
153
177
  );
@@ -5,7 +5,6 @@ import * as TestConsole from "effect/testing/TestConsole";
5
5
  import { withTestTransaction } from "../testing/transaction.js";
6
6
  import type {
7
7
  AnyDatabase,
8
- DatabaseRequirement,
9
8
  DatabaseService,
10
9
  DatabaseTest,
11
10
  DatabaseTester,
@@ -76,18 +75,10 @@ const restoreContext = <Provided>(
76
75
  }) as DatabaseFixture<Provided>;
77
76
 
78
77
  export const makeDatabaseTester = <Database extends AnyDatabase, Provided>(
79
- fixtureIt: FixtureTestApi<
80
- Provided | DatabaseRequirement<Database> | Effect.Services<Database>
81
- >,
78
+ fixtureIt: FixtureTestApi<Provided | Effect.Services<Database>>,
82
79
  database: Database,
83
- ): DatabaseTester<
84
- Database,
85
- Provided | DatabaseRequirement<Database> | Effect.Services<Database>
86
- > => {
87
- type Services =
88
- | Provided
89
- | DatabaseRequirement<Database>
90
- | Effect.Services<Database>;
80
+ ): DatabaseTester<Database, Provided | Effect.Services<Database>> => {
81
+ type Services = Provided | Effect.Services<Database>;
91
82
 
92
83
  const run = <A, Eff extends Effect.Effect<unknown, unknown, Services>>(
93
84
  body: (
@@ -16,9 +16,9 @@ type Simplify<Value> = { [Key in keyof Value]: Value[Key] };
16
16
  type QueryValue<Query> =
17
17
  Query extends RelationQuery<
18
18
  infer Value,
19
- infer _Requirement,
20
19
  infer _Contract,
21
- infer _Model
20
+ infer _Model,
21
+ infer _DatabaseId
22
22
  >
23
23
  ? Value
24
24
  : never;
@@ -27,21 +27,27 @@ type AcceptRelatedQuery<
27
27
  Query,
28
28
  Contract extends AnyPostgresContract,
29
29
  Model extends string,
30
- > =
31
- Query extends RelationQuery<
30
+ DatabaseId,
31
+ > = [Query] extends [
32
+ RelationQuery<
32
33
  infer _Value,
33
- infer _Requirement,
34
34
  infer QueryContract,
35
- infer QueryModel
36
- >
37
- ? [QueryContract] extends [Contract]
38
- ? [Contract] extends [QueryContract]
39
- ? QueryModel extends Model
40
- ? unknown
35
+ infer QueryModel,
36
+ infer QueryDatabaseId
37
+ >,
38
+ ]
39
+ ? [QueryContract] extends [Contract]
40
+ ? [Contract] extends [QueryContract]
41
+ ? [QueryDatabaseId] extends [DatabaseId]
42
+ ? [DatabaseId] extends [QueryDatabaseId]
43
+ ? QueryModel extends Model
44
+ ? unknown
45
+ : never
41
46
  : never
42
47
  : never
43
48
  : never
44
- : never;
49
+ : never
50
+ : never;
45
51
 
46
52
  type IncludedQueryValue<
47
53
  Query,
@@ -61,9 +67,10 @@ type AcceptRelatedQueryShape<
61
67
  Shape extends Readonly<Record<string, unknown>>,
62
68
  Contract extends AnyPostgresContract,
63
69
  Model extends string,
70
+ DatabaseId,
64
71
  > = {
65
72
  readonly [Key in keyof Shape]: Shape[Key] &
66
- AcceptRelatedQuery<Shape[Key], Contract, Model>;
73
+ AcceptRelatedQuery<Shape[Key], Contract, Model, DatabaseId>;
67
74
  };
68
75
 
69
76
  type WithIncludedRelation<
@@ -84,9 +91,9 @@ type WithIncludedRelation<
84
91
 
85
92
  export type IncludeMethod<
86
93
  Collection,
87
- Requirement,
88
94
  Contract,
89
95
  Model extends string,
96
+ DatabaseId,
90
97
  > = Contract extends AnyPostgresContract
91
98
  ? Collection extends PrismaCollection<
92
99
  Contract,
@@ -113,9 +120,9 @@ export type IncludeMethod<
113
120
  >
114
121
  >
115
122
  >,
116
- Requirement,
117
123
  Contract,
118
- Model
124
+ Model,
125
+ DatabaseId
119
126
  >;
120
127
  include<RelationName extends RelationNames<Contract, Model>, Query>(
121
128
  relationName: RelationName,
@@ -123,7 +130,8 @@ export type IncludeMethod<
123
130
  AcceptRelatedQuery<
124
131
  Query,
125
132
  Contract,
126
- RelatedModelNameOf<Contract, Model, RelationName>
133
+ RelatedModelNameOf<Contract, Model, RelationName>,
134
+ DatabaseId
127
135
  >,
128
136
  ): Relation<
129
137
  WithIncludedRelation<
@@ -133,9 +141,9 @@ export type IncludeMethod<
133
141
  RelationName,
134
142
  IncludedQueryValue<Query, Contract, Model, RelationName>
135
143
  >,
136
- Requirement,
137
144
  Contract,
138
- Model
145
+ Model,
146
+ DatabaseId
139
147
  >;
140
148
  include<
141
149
  RelationName extends RelationNames<Contract, Model>,
@@ -149,7 +157,8 @@ export type IncludeMethod<
149
157
  AcceptRelatedQueryShape<
150
158
  Shape,
151
159
  Contract,
152
- RelatedModelNameOf<Contract, Model, RelationName>
160
+ RelatedModelNameOf<Contract, Model, RelationName>,
161
+ DatabaseId
153
162
  >
154
163
  : never,
155
164
  ): Relation<
@@ -160,9 +169,9 @@ export type IncludeMethod<
160
169
  RelationName,
161
170
  QueryShapeValue<Shape>
162
171
  >,
163
- Requirement,
164
172
  Contract,
165
- Model
173
+ Model,
174
+ DatabaseId
166
175
  >;
167
176
  }
168
177
  : Record<never, never>