@shivaedev/effect-prisma 0.6.3 → 0.7.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.
- package/CHANGELOG.md +26 -1
- package/dist/bin/normalize-contract.js +1 -0
- package/dist/bin/normalize-contract.js.map +1 -1
- package/dist/database.d.ts +4 -4
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +12 -12
- package/dist/database.js.map +1 -1
- package/dist/error.d.ts +5 -5
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +5 -6
- package/dist/error.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/internal/adapters/transaction-settlement.d.ts +10 -0
- package/dist/internal/adapters/transaction-settlement.d.ts.map +1 -0
- package/dist/internal/adapters/transaction-settlement.js +56 -0
- package/dist/internal/adapters/transaction-settlement.js.map +1 -0
- package/dist/internal/client-lifecycle.d.ts +2 -2
- package/dist/internal/client-lifecycle.d.ts.map +1 -1
- package/dist/internal/client-lifecycle.js.map +1 -1
- package/dist/internal/contract-normalization.d.ts +0 -4
- package/dist/internal/contract-normalization.d.ts.map +1 -1
- package/dist/internal/contract-normalization.js +5 -10
- package/dist/internal/contract-normalization.js.map +1 -1
- package/dist/internal/database-facade.d.ts +19 -0
- package/dist/internal/database-facade.d.ts.map +1 -0
- package/dist/internal/database-facade.js +79 -0
- package/dist/internal/database-facade.js.map +1 -0
- package/dist/internal/database-factory.d.ts +4 -48
- package/dist/internal/database-factory.d.ts.map +1 -1
- package/dist/internal/database-factory.js +11 -90
- package/dist/internal/database-factory.js.map +1 -1
- package/dist/internal/database-types.d.ts +33 -0
- package/dist/internal/database-types.d.ts.map +1 -0
- package/dist/internal/database-types.js +2 -0
- package/dist/internal/database-types.js.map +1 -0
- package/dist/internal/dynamic.d.ts +5 -0
- package/dist/internal/dynamic.d.ts.map +1 -0
- package/dist/internal/dynamic.js +12 -0
- package/dist/internal/dynamic.js.map +1 -0
- package/dist/internal/executor.d.ts +3 -7
- package/dist/internal/executor.d.ts.map +1 -1
- package/dist/internal/promise.d.ts +1 -1
- package/dist/internal/promise.d.ts.map +1 -1
- package/dist/internal/promise.js +2 -4
- package/dist/internal/promise.js.map +1 -1
- package/dist/internal/query-execution.d.ts +2 -2
- package/dist/internal/query-execution.d.ts.map +1 -1
- package/dist/internal/query-execution.js +2 -6
- package/dist/internal/query-execution.js.map +1 -1
- package/dist/internal/recipe.d.ts +3 -3
- package/dist/internal/recipe.d.ts.map +1 -1
- package/dist/internal/recipe.js +9 -20
- package/dist/internal/recipe.js.map +1 -1
- package/dist/internal/relation-plan.d.ts +1 -1
- package/dist/internal/relation-result.d.ts +4 -0
- package/dist/internal/relation-result.d.ts.map +1 -0
- package/dist/internal/relation-result.js +19 -0
- package/dist/internal/relation-result.js.map +1 -0
- package/dist/internal/relation-runtime.d.ts +3 -3
- package/dist/internal/relation-runtime.d.ts.map +1 -1
- package/dist/internal/relation-runtime.js +12 -37
- package/dist/internal/relation-runtime.js.map +1 -1
- package/dist/internal/relation-stream.d.ts +3 -3
- package/dist/internal/relation-stream.d.ts.map +1 -1
- package/dist/internal/relation-stream.js +20 -24
- package/dist/internal/relation-stream.js.map +1 -1
- package/dist/internal/sqlite-datetime.d.ts +1 -27
- package/dist/internal/sqlite-datetime.d.ts.map +1 -1
- package/dist/internal/sqlite-datetime.js +16 -35
- package/dist/internal/sqlite-datetime.js.map +1 -1
- package/dist/internal/sqlite-pragmas.d.ts +1 -13
- package/dist/internal/sqlite-pragmas.d.ts.map +1 -1
- package/dist/internal/sqlite-pragmas.js +2 -12
- package/dist/internal/sqlite-pragmas.js.map +1 -1
- package/dist/internal/testing.d.ts +1 -1
- package/dist/internal/testing.d.ts.map +1 -1
- package/dist/internal/testing.js +5 -2
- package/dist/internal/testing.js.map +1 -1
- package/dist/internal/transaction.d.ts +5 -5
- package/dist/internal/transaction.d.ts.map +1 -1
- package/dist/internal/transaction.js +5 -53
- package/dist/internal/transaction.js.map +1 -1
- package/dist/relation/include-metadata.d.ts +1 -1
- package/dist/relation/include-metadata.d.ts.map +1 -1
- package/dist/relation/include.d.ts +8 -5
- package/dist/relation/include.d.ts.map +1 -1
- package/dist/relation/prisma-methods.d.ts +17 -17
- package/dist/relation/prisma-methods.d.ts.map +1 -1
- package/dist/relation.d.ts +4 -4
- package/dist/relation.d.ts.map +1 -1
- package/dist/sqlite.d.ts +5 -10
- package/dist/sqlite.d.ts.map +1 -1
- package/dist/sqlite.js +14 -12
- package/dist/sqlite.js.map +1 -1
- package/dist/testing/transaction.d.ts +6 -7
- package/dist/testing/transaction.d.ts.map +1 -1
- package/dist/testing/transaction.js +2 -2
- package/dist/testing/transaction.js.map +1 -1
- package/dist/testing/types.d.ts +7 -7
- package/dist/testing/types.d.ts.map +1 -1
- package/dist/testing/vitest.d.ts +3 -3
- package/dist/testing/vitest.d.ts.map +1 -1
- package/dist/testing/vitest.js +2 -2
- package/dist/testing/vitest.js.map +1 -1
- package/dist/testing.d.ts +3 -3
- package/package.json +35 -37
- package/src/bin/normalize-contract.ts +2 -1
- package/src/database.ts +49 -67
- package/src/error.ts +13 -23
- package/src/index.ts +3 -3
- package/src/internal/adapters/transaction-settlement.ts +70 -0
- package/src/internal/client-lifecycle.ts +4 -11
- package/src/internal/contract-normalization.ts +6 -14
- package/src/internal/database-facade.ts +138 -0
- package/src/internal/database-factory.ts +34 -306
- package/src/internal/database-types.ts +65 -0
- package/src/internal/dynamic.ts +16 -0
- package/src/internal/executor.ts +4 -11
- package/src/internal/promise.ts +4 -12
- package/src/internal/query-execution.ts +4 -8
- package/src/internal/recipe.ts +24 -89
- package/src/internal/relation-plan.ts +1 -1
- package/src/internal/relation-result.ts +27 -0
- package/src/internal/relation-runtime.ts +45 -135
- package/src/internal/relation-stream.ts +24 -51
- package/src/internal/sqlite-datetime.ts +20 -54
- package/src/internal/sqlite-pragmas.ts +3 -16
- package/src/internal/testing.ts +13 -15
- package/src/internal/transaction.ts +16 -109
- package/src/relation/include-metadata.ts +11 -43
- package/src/relation/include.ts +46 -140
- package/src/relation/prisma-methods.ts +36 -122
- package/src/relation.ts +20 -48
- package/src/sqlite.ts +59 -85
- package/src/testing/transaction.ts +20 -33
- package/src/testing/types.ts +15 -34
- package/src/testing/vitest.ts +14 -28
- package/src/testing.ts +3 -3
|
@@ -1,328 +1,56 @@
|
|
|
1
|
-
import type { orm } from "@prisma-next/sql-orm-client";
|
|
2
1
|
import { Context, Effect, Layer } from "effect";
|
|
3
|
-
import {
|
|
4
|
-
import type
|
|
5
|
-
import { assertAvailableModelNames } from "./client-lifecycle.js";
|
|
6
|
-
import type { AnySqlContract, DatabaseExecutor } from "./executor.js";
|
|
7
|
-
import { makeModelRelation } from "./relation-runtime.js";
|
|
8
|
-
import { DatabaseTestingTypeId } from "./testing.js";
|
|
2
|
+
import type { PrismaError } from "../error.ts";
|
|
3
|
+
import { type ActiveTransaction, type FacadeScope, makeFacade, runTransaction } from "./database-facade.ts";
|
|
9
4
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
type
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
type DatabaseIdentifier,
|
|
6
|
+
type DatabaseIdentifierLiteral,
|
|
7
|
+
type DatabaseService,
|
|
8
|
+
type DefaultModels,
|
|
9
|
+
internalContextIdentifierPrefix,
|
|
10
|
+
type SqlDatabase,
|
|
11
|
+
} from "./database-types.ts";
|
|
12
|
+
import type { AnySqlContract, DatabaseExecutor } from "./executor.ts";
|
|
13
|
+
import { DatabaseTestingTypeId } from "./testing.ts";
|
|
14
|
+
import { releaseTestTransaction } from "./transaction.ts";
|
|
16
15
|
|
|
17
|
-
type OrmFor<Contract extends AnySqlContract> = ReturnType<typeof orm<Contract>>;
|
|
18
|
-
type DefaultNamespaceId<Contract extends AnySqlContract> =
|
|
19
|
-
"__unbound__" extends keyof OrmFor<Contract>
|
|
20
|
-
? "__unbound__"
|
|
21
|
-
: "public" extends keyof OrmFor<Contract>
|
|
22
|
-
? "public"
|
|
23
|
-
: keyof OrmFor<Contract>;
|
|
24
|
-
export type DefaultModels<Contract extends AnySqlContract> =
|
|
25
|
-
OrmFor<Contract>[DefaultNamespaceId<Contract>] extends infer Models extends
|
|
26
|
-
object
|
|
27
|
-
? Models
|
|
28
|
-
: never;
|
|
29
|
-
|
|
30
|
-
type IsUnion<Value, Whole = Value> = Value extends unknown
|
|
31
|
-
? [Whole] extends [Value]
|
|
32
|
-
? false
|
|
33
|
-
: true
|
|
34
|
-
: never;
|
|
35
|
-
|
|
36
|
-
type IsSingletonString<Value extends string> =
|
|
37
|
-
Record<never, never> extends Record<Value, never> ? false : true;
|
|
38
|
-
|
|
39
|
-
const internalContextIdentifierPrefix = "\0@shivaedev/effect-prisma/internal/";
|
|
40
16
|
let activeTransactionReferenceSequence = 0;
|
|
41
17
|
|
|
42
|
-
export
|
|
43
|
-
Identifier,
|
|
44
|
-
] extends [`\0@shivaedev/effect-prisma/internal/${string}`]
|
|
45
|
-
? never
|
|
46
|
-
: string extends Identifier
|
|
47
|
-
? never
|
|
48
|
-
: true extends IsUnion<Identifier>
|
|
49
|
-
? never
|
|
50
|
-
: IsSingletonString<Identifier> extends true
|
|
51
|
-
? Identifier
|
|
52
|
-
: never;
|
|
53
|
-
|
|
54
|
-
export interface DatabaseIdentifier<
|
|
55
|
-
Contract extends AnySqlContract,
|
|
56
|
-
Identifier extends string,
|
|
57
|
-
> {
|
|
58
|
-
readonly _contract: (contract: Contract) => Contract;
|
|
59
|
-
readonly _databaseIdentifier: (identifier: Identifier) => Identifier;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
type DatabaseModels<
|
|
63
|
-
Contract extends AnySqlContract,
|
|
64
|
-
Identifier extends string,
|
|
65
|
-
> = {
|
|
66
|
-
readonly [Model in keyof DefaultModels<Contract>]: Relation<
|
|
67
|
-
DefaultModels<Contract>[Model],
|
|
68
|
-
Contract,
|
|
69
|
-
Model & string,
|
|
70
|
-
DatabaseIdentifier<Contract, Identifier>
|
|
71
|
-
>;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export type DatabaseService<
|
|
75
|
-
Contract extends AnySqlContract,
|
|
76
|
-
Identifier extends string,
|
|
77
|
-
> = DatabaseModels<Contract, Identifier> & {
|
|
78
|
-
transaction<A, E, R>(
|
|
79
|
-
program: Effect.Effect<A, E, R> &
|
|
80
|
-
(DatabaseIdentifier<Contract, Identifier> extends R ? unknown : never),
|
|
81
|
-
): Effect.Effect<
|
|
82
|
-
A,
|
|
83
|
-
E | PrismaError,
|
|
84
|
-
Exclude<R, DatabaseIdentifier<Contract, Identifier>>
|
|
85
|
-
>;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* The driver-independent part of a database definition: the Context service
|
|
90
|
-
* carrying the typed facade. Driver entrypoints add their own `layer` options.
|
|
91
|
-
*/
|
|
92
|
-
export interface DatabaseServiceHolder<
|
|
93
|
-
Contract extends AnySqlContract,
|
|
94
|
-
Identifier extends string,
|
|
95
|
-
> extends Context.Service<
|
|
96
|
-
DatabaseIdentifier<Contract, Identifier>,
|
|
97
|
-
DatabaseService<Contract, Identifier>
|
|
98
|
-
> {}
|
|
99
|
-
|
|
100
|
-
export type AnyDatabase = Effect.Effect<unknown, never, unknown> & {
|
|
101
|
-
readonly layer: (...arguments_: ReadonlyArray<never>) => Layer.Any;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export type DatabaseServiceOf<Database extends AnyDatabase> =
|
|
105
|
-
Effect.Success<Database>;
|
|
106
|
-
|
|
107
|
-
export interface SqlDatabase<
|
|
108
|
-
Contract extends AnySqlContract,
|
|
109
|
-
Identifier extends string,
|
|
110
|
-
Options,
|
|
111
|
-
> extends DatabaseServiceHolder<Contract, Identifier> {
|
|
112
|
-
readonly layer: (
|
|
113
|
-
options: Options,
|
|
114
|
-
) => Layer.Layer<DatabaseIdentifier<Contract, Identifier>, PrismaError>;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Resolve the models of the single domain namespace from a namespaced ORM
|
|
119
|
-
* client. Prisma Next namespaces PostgreSQL models under their schema and
|
|
120
|
-
* SQLite models under the unbound namespace.
|
|
121
|
-
*/
|
|
122
|
-
export const namespaceModels = <
|
|
123
|
-
Contract extends AnySqlContract,
|
|
124
|
-
Models extends object,
|
|
125
|
-
>(
|
|
126
|
-
contract: Contract,
|
|
127
|
-
namespacedOrm: TransactionOrm<Contract>,
|
|
128
|
-
): Models => {
|
|
129
|
-
const namespaces = Object.keys(contract.domain.namespaces);
|
|
130
|
-
if (namespaces.length !== 1 || namespaces[0] === undefined) {
|
|
131
|
-
throw new TypeError(
|
|
132
|
-
"Effect Prisma currently requires exactly one domain namespace",
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
assertAvailableModelNames(
|
|
136
|
-
Object.keys(contract.domain.namespaces[namespaces[0]].models),
|
|
137
|
-
);
|
|
138
|
-
return Reflect.get(namespacedOrm, namespaces[0]) as Models;
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Assemble the database service and its Layer around a driver-specific
|
|
143
|
-
* executor acquisition.
|
|
144
|
-
*/
|
|
145
|
-
export const makeSqlDatabase = <
|
|
146
|
-
const Contract extends AnySqlContract,
|
|
147
|
-
const Identifier extends string,
|
|
148
|
-
Options,
|
|
149
|
-
>(
|
|
18
|
+
export const makeSqlDatabase = <const Contract extends AnySqlContract, const Identifier extends string, Options>(
|
|
150
19
|
identifier: DatabaseIdentifierLiteral<Identifier>,
|
|
151
|
-
acquireExecutor: (
|
|
152
|
-
options: Options,
|
|
153
|
-
) => Effect.Effect<
|
|
154
|
-
DatabaseExecutor<DefaultModels<Contract>, Contract>,
|
|
155
|
-
PrismaError
|
|
156
|
-
>,
|
|
20
|
+
acquireExecutor: (options: Options) => Effect.Effect<DatabaseExecutor<DefaultModels<Contract>, Contract>, PrismaError>,
|
|
157
21
|
): SqlDatabase<Contract, Identifier, Options> => {
|
|
158
22
|
if (identifier.startsWith(internalContextIdentifierPrefix)) {
|
|
159
|
-
throw new TypeError(
|
|
160
|
-
"Database identifier uses a reserved internal namespace",
|
|
161
|
-
);
|
|
23
|
+
throw new TypeError("Database identifier uses a reserved internal namespace");
|
|
162
24
|
}
|
|
163
25
|
type Models = DefaultModels<Contract>;
|
|
164
26
|
type DatabaseId = DatabaseIdentifier<Contract, Identifier>;
|
|
165
27
|
|
|
166
|
-
const Service = Context.Service<
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
type ActiveTransaction = {
|
|
175
|
-
readonly executor: DatabaseExecutor<Models, Contract>;
|
|
176
|
-
readonly facade: DatabaseService<Contract, Identifier>;
|
|
28
|
+
const Service = Context.Service<DatabaseId, DatabaseService<Contract, Identifier>>(identifier);
|
|
29
|
+
const scope: FacadeScope<Contract, Identifier> = {
|
|
30
|
+
activeTransaction: Context.Reference<ActiveTransaction<Contract, Identifier> | undefined>(
|
|
31
|
+
`${internalContextIdentifierPrefix}ActiveTransaction/${activeTransactionReferenceSequence++}`,
|
|
32
|
+
{ defaultValue: () => undefined },
|
|
33
|
+
),
|
|
34
|
+
executors: new WeakMap(),
|
|
35
|
+
service: Service,
|
|
177
36
|
};
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
{ defaultValue: () => undefined },
|
|
181
|
-
);
|
|
182
|
-
const executorOf = (
|
|
183
|
-
facade: DatabaseService<Contract, Identifier>,
|
|
184
|
-
): Effect.Effect<DatabaseExecutor<Models, Contract>> => {
|
|
185
|
-
const executor = executors.get(facade);
|
|
37
|
+
const executorOf = (facade: DatabaseService<Contract, Identifier>): Effect.Effect<DatabaseExecutor<Models, Contract>> => {
|
|
38
|
+
const executor = scope.executors.get(facade);
|
|
186
39
|
return executor === undefined
|
|
187
|
-
? Effect.die(
|
|
188
|
-
new TypeError(
|
|
189
|
-
"The database service was not created by its database Layer",
|
|
190
|
-
),
|
|
191
|
-
)
|
|
40
|
+
? Effect.die(new TypeError("The database service was not created by its database Layer"))
|
|
192
41
|
: Effect.succeed(executor);
|
|
193
42
|
};
|
|
194
|
-
const runTransaction = <A, E, R>(
|
|
195
|
-
current: DatabaseExecutor<Models, Contract>,
|
|
196
|
-
currentFacade: DatabaseService<Contract, Identifier>,
|
|
197
|
-
program: Effect.Effect<A, E, R>,
|
|
198
|
-
release: typeof releaseTransaction,
|
|
199
|
-
mode: "test" | "transaction",
|
|
200
|
-
span: string,
|
|
201
|
-
): Effect.Effect<A, E | PrismaError, Exclude<R, DatabaseId>> => {
|
|
202
|
-
if (!current.liveness.open) {
|
|
203
|
-
return Effect.fail(toPrismaError({ code: current.liveness.closedCode }));
|
|
204
|
-
}
|
|
205
|
-
if (
|
|
206
|
-
current.mode === "test" ||
|
|
207
|
-
(current.mode === "transaction" && mode === "transaction")
|
|
208
|
-
) {
|
|
209
|
-
return Effect.provideService(program, Service, currentFacade);
|
|
210
|
-
}
|
|
211
|
-
if (current.mode === "transaction") {
|
|
212
|
-
return Effect.fail(
|
|
213
|
-
toPrismaError({
|
|
214
|
-
code: "RUNTIME.TEST_TRANSACTION_INSIDE_TRANSACTION_UNSUPPORTED",
|
|
215
|
-
}),
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const transaction = Effect.acquireUseRelease(
|
|
220
|
-
acquireTransaction(
|
|
221
|
-
current,
|
|
222
|
-
(transactionOrm) =>
|
|
223
|
-
namespaceModels<Contract, Models>(
|
|
224
|
-
current.client.contract,
|
|
225
|
-
transactionOrm,
|
|
226
|
-
),
|
|
227
|
-
mode,
|
|
228
|
-
),
|
|
229
|
-
(resource) => {
|
|
230
|
-
const transactionFacade = makeFacade(resource.executor);
|
|
231
|
-
return program.pipe(
|
|
232
|
-
Effect.provideService(Service, transactionFacade),
|
|
233
|
-
Effect.provideService(ActiveTransaction, {
|
|
234
|
-
executor: resource.executor,
|
|
235
|
-
facade: transactionFacade,
|
|
236
|
-
}),
|
|
237
|
-
);
|
|
238
|
-
},
|
|
239
|
-
release,
|
|
240
|
-
).pipe(Effect.withSpan(span, { kind: "client" }));
|
|
241
|
-
|
|
242
|
-
return withTransactionSemaphore(current.transactionSemaphore, transaction);
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
function makeFacade(
|
|
246
|
-
current: DatabaseExecutor<Models, Contract>,
|
|
247
|
-
): DatabaseService<Contract, Identifier> {
|
|
248
|
-
let facade: DatabaseService<Contract, Identifier>;
|
|
249
|
-
const resolveActiveTransaction = Effect.flatMap(
|
|
250
|
-
ActiveTransaction,
|
|
251
|
-
(active) => {
|
|
252
|
-
if (current.mode === "root") {
|
|
253
|
-
return Effect.succeed(active ?? { executor: current, facade });
|
|
254
|
-
}
|
|
255
|
-
return active?.executor === current
|
|
256
|
-
? Effect.succeed(active)
|
|
257
|
-
: Effect.fail(
|
|
258
|
-
toPrismaError({
|
|
259
|
-
code: "RUNTIME.TRANSACTION_CONTEXT_MISMATCH",
|
|
260
|
-
}),
|
|
261
|
-
);
|
|
262
|
-
},
|
|
263
|
-
);
|
|
264
|
-
const resolveExecutor = Effect.suspend(() =>
|
|
265
|
-
current.liveness.open
|
|
266
|
-
? Effect.map(resolveActiveTransaction, ({ executor }) => executor)
|
|
267
|
-
: Effect.succeed(current),
|
|
268
|
-
);
|
|
269
|
-
const target = Object.assign(Object.create(null), {
|
|
270
|
-
transaction: <A, E, R>(
|
|
271
|
-
program: Effect.Effect<A, E, R> &
|
|
272
|
-
(DatabaseId extends R ? unknown : never),
|
|
273
|
-
) =>
|
|
274
|
-
Effect.suspend(() => {
|
|
275
|
-
if (!current.liveness.open) {
|
|
276
|
-
return Effect.fail(
|
|
277
|
-
toPrismaError({ code: current.liveness.closedCode }),
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
return Effect.flatMap(resolveActiveTransaction, (selected) => {
|
|
281
|
-
return runTransaction(
|
|
282
|
-
selected.executor,
|
|
283
|
-
selected.facade,
|
|
284
|
-
program,
|
|
285
|
-
releaseTransaction,
|
|
286
|
-
"transaction",
|
|
287
|
-
"prisma.transaction",
|
|
288
|
-
);
|
|
289
|
-
});
|
|
290
|
-
}),
|
|
291
|
-
}) as DatabaseService<Contract, Identifier>;
|
|
292
|
-
facade = new Proxy(target, {
|
|
293
|
-
get(target, property, receiver) {
|
|
294
|
-
if (Reflect.has(target, property)) {
|
|
295
|
-
return Reflect.get(target, property, receiver);
|
|
296
|
-
}
|
|
297
|
-
if (typeof property !== "string") {
|
|
298
|
-
return undefined;
|
|
299
|
-
}
|
|
300
|
-
return makeModelRelation(current, property, resolveExecutor);
|
|
301
|
-
},
|
|
302
|
-
});
|
|
303
|
-
executors.set(facade, current);
|
|
304
|
-
return facade;
|
|
305
|
-
}
|
|
306
43
|
|
|
307
44
|
const withTestTransaction = <A, E, R>(
|
|
308
45
|
program: Effect.Effect<A, E, R> & (DatabaseId extends R ? unknown : never),
|
|
309
46
|
): Effect.Effect<A, E | PrismaError, DatabaseId | Exclude<R, DatabaseId>> =>
|
|
310
|
-
Effect.flatMap(Service, (facade) =>
|
|
311
|
-
|
|
312
|
-
runTransaction(
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
program,
|
|
316
|
-
releaseTestTransaction,
|
|
317
|
-
"test",
|
|
318
|
-
"prisma.testTransaction",
|
|
319
|
-
),
|
|
320
|
-
);
|
|
321
|
-
});
|
|
47
|
+
Effect.flatMap(Service, (facade) =>
|
|
48
|
+
Effect.flatMap(executorOf(facade), (current) =>
|
|
49
|
+
runTransaction(scope, current, facade, program, releaseTestTransaction, "test", "prisma.testTransaction"),
|
|
50
|
+
),
|
|
51
|
+
);
|
|
322
52
|
|
|
323
|
-
const layer = (
|
|
324
|
-
layerOptions: Options,
|
|
325
|
-
): Layer.Layer<DatabaseId, PrismaError> => {
|
|
53
|
+
const layer = (layerOptions: Options): Layer.Layer<DatabaseId, PrismaError> => {
|
|
326
54
|
const acquire = Effect.acquireRelease(
|
|
327
55
|
Effect.suspend(() => acquireExecutor(layerOptions)),
|
|
328
56
|
(executor) => {
|
|
@@ -331,11 +59,11 @@ export const makeSqlDatabase = <
|
|
|
331
59
|
},
|
|
332
60
|
);
|
|
333
61
|
|
|
334
|
-
return Layer.effect(Service)(Effect.map(acquire, makeFacade));
|
|
62
|
+
return Layer.effect(Service)(Effect.map(acquire, (executor) => makeFacade(scope, executor)));
|
|
335
63
|
};
|
|
336
64
|
|
|
337
65
|
return Object.assign(Service, {
|
|
338
66
|
layer,
|
|
339
67
|
[DatabaseTestingTypeId]: { withTestTransaction },
|
|
340
|
-
})
|
|
68
|
+
});
|
|
341
69
|
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { orm } from "@prisma-next/sql-orm-client";
|
|
2
|
+
import type { Context, Effect, Layer } from "effect";
|
|
3
|
+
import type { PrismaError } from "../error.ts";
|
|
4
|
+
import type { Relation } from "../relation.ts";
|
|
5
|
+
import type { AnySqlContract } from "./executor.ts";
|
|
6
|
+
|
|
7
|
+
type OrmFor<Contract extends AnySqlContract> = ReturnType<typeof orm<Contract>>;
|
|
8
|
+
type DefaultNamespaceId<Contract extends AnySqlContract> = "__unbound__" extends keyof OrmFor<Contract>
|
|
9
|
+
? "__unbound__"
|
|
10
|
+
: "public" extends keyof OrmFor<Contract>
|
|
11
|
+
? "public"
|
|
12
|
+
: keyof OrmFor<Contract>;
|
|
13
|
+
export type DefaultModels<Contract extends AnySqlContract> = OrmFor<Contract>[DefaultNamespaceId<Contract>] extends infer Models extends object
|
|
14
|
+
? Models
|
|
15
|
+
: never;
|
|
16
|
+
|
|
17
|
+
type IsUnion<Value, Whole = Value> = Value extends unknown ? ([Whole] extends [Value] ? false : true) : never;
|
|
18
|
+
|
|
19
|
+
type IsSingletonString<Value extends string> = Record<never, never> extends Record<Value, never> ? false : true;
|
|
20
|
+
|
|
21
|
+
export const internalContextIdentifierPrefix = "\0@shivaedev/effect-prisma/internal/";
|
|
22
|
+
|
|
23
|
+
export type DatabaseIdentifierLiteral<Identifier extends string> = [Identifier] extends [`\0@shivaedev/effect-prisma/internal/${string}`]
|
|
24
|
+
? never
|
|
25
|
+
: string extends Identifier
|
|
26
|
+
? never
|
|
27
|
+
: true extends IsUnion<Identifier>
|
|
28
|
+
? never
|
|
29
|
+
: IsSingletonString<Identifier> extends true
|
|
30
|
+
? Identifier
|
|
31
|
+
: never;
|
|
32
|
+
|
|
33
|
+
export interface DatabaseIdentifier<Contract extends AnySqlContract, Identifier extends string> {
|
|
34
|
+
readonly _contract: (contract: Contract) => Contract;
|
|
35
|
+
readonly _databaseIdentifier: (identifier: Identifier) => Identifier;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type DatabaseModels<Contract extends AnySqlContract, Identifier extends string> = {
|
|
39
|
+
readonly [Model in keyof DefaultModels<Contract>]: Relation<
|
|
40
|
+
DefaultModels<Contract>[Model],
|
|
41
|
+
Contract,
|
|
42
|
+
Model & string,
|
|
43
|
+
DatabaseIdentifier<Contract, Identifier>
|
|
44
|
+
>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type DatabaseService<Contract extends AnySqlContract, Identifier extends string> = DatabaseModels<Contract, Identifier> & {
|
|
48
|
+
transaction: <A, E, R>(
|
|
49
|
+
program: Effect.Effect<A, E, R> & (DatabaseIdentifier<Contract, Identifier> extends R ? unknown : never),
|
|
50
|
+
) => Effect.Effect<A, E | PrismaError, Exclude<R, DatabaseIdentifier<Contract, Identifier>>>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export interface DatabaseServiceHolder<Contract extends AnySqlContract, Identifier extends string>
|
|
54
|
+
extends Context.Service<DatabaseIdentifier<Contract, Identifier>, DatabaseService<Contract, Identifier>> {}
|
|
55
|
+
|
|
56
|
+
export type AnyDatabase = Effect.Effect<unknown, never, unknown> & {
|
|
57
|
+
readonly layer: (...arguments_: readonly never[]) => Layer.Any;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type DatabaseServiceOf<Database extends AnyDatabase> = Effect.Success<Database>;
|
|
61
|
+
|
|
62
|
+
export interface SqlDatabase<Contract extends AnySqlContract, Identifier extends string, Options>
|
|
63
|
+
extends DatabaseServiceHolder<Contract, Identifier> {
|
|
64
|
+
readonly layer: (options: Options) => Layer.Layer<DatabaseIdentifier<Contract, Identifier>, PrismaError>;
|
|
65
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const isObjectLike = (value: unknown): value is object => (typeof value === "object" && value !== null) || typeof value === "function";
|
|
2
|
+
|
|
3
|
+
export const hasMethod = (value: unknown, name: PropertyKey): value is object =>
|
|
4
|
+
isObjectLike(value) && typeof Reflect.get(value, name) === "function";
|
|
5
|
+
|
|
6
|
+
export const invokeMethod = (target: object, name: PropertyKey, arguments_: readonly unknown[]): unknown => {
|
|
7
|
+
const method: unknown = Reflect.get(target, name);
|
|
8
|
+
if (typeof method !== "function") {
|
|
9
|
+
throw new TypeError(`${String(name)} is not a method`);
|
|
10
|
+
}
|
|
11
|
+
return Reflect.apply(method, target, arguments_);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const isPromiseLike = (value: unknown): value is PromiseLike<unknown> => hasMethod(value, "then");
|
|
15
|
+
|
|
16
|
+
export const isAsyncIterable = (value: unknown): value is AsyncIterable<unknown> => hasMethod(value, Symbol.asyncIterator);
|
package/src/internal/executor.ts
CHANGED
|
@@ -6,21 +6,14 @@ import type { Semaphore } from "effect";
|
|
|
6
6
|
export type AnySqlContract = PrismaContract<SqlStorage>;
|
|
7
7
|
export type AnyPostgresContract = AnySqlContract;
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* The subset of a Prisma Next client the package depends on. Both the
|
|
11
|
-
* PostgreSQL and the SQLite client satisfy it.
|
|
12
|
-
*/
|
|
13
9
|
export interface SqlDatabaseClient<Contract extends AnySqlContract> {
|
|
14
|
-
|
|
10
|
+
close: () => Promise<void>;
|
|
15
11
|
readonly context: ExecutionContext<Contract>;
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
readonly contract: Contract;
|
|
13
|
+
runtime: () => Runtime;
|
|
18
14
|
}
|
|
19
15
|
|
|
20
|
-
export interface DatabaseExecutor<
|
|
21
|
-
Models extends object,
|
|
22
|
-
Contract extends AnySqlContract = AnySqlContract,
|
|
23
|
-
> {
|
|
16
|
+
export interface DatabaseExecutor<Models extends object, Contract extends AnySqlContract = AnySqlContract> {
|
|
24
17
|
readonly client: SqlDatabaseClient<Contract>;
|
|
25
18
|
readonly identity: object;
|
|
26
19
|
readonly liveness: {
|
package/src/internal/promise.ts
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { isPrismaFailure, type PrismaError, toPrismaError } from "../error.
|
|
2
|
+
import { isPrismaFailure, type PrismaError, toPrismaError } from "../error.ts";
|
|
3
3
|
|
|
4
|
-
export const fromPrismaPromise = <A>(
|
|
5
|
-
evaluate: (signal: AbortSignal) => PromiseLike<A>,
|
|
6
|
-
): Effect.Effect<A, PrismaError> =>
|
|
4
|
+
export const fromPrismaPromise = <A>(evaluate: (signal: AbortSignal) => PromiseLike<A>): Effect.Effect<A, PrismaError> =>
|
|
7
5
|
Effect.tryPromise({
|
|
8
|
-
try: (signal) => Promise.resolve(evaluate(signal)),
|
|
9
6
|
catch: (error) => error,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
isPrismaFailure(error)
|
|
13
|
-
? Effect.fail(toPrismaError(error))
|
|
14
|
-
: Effect.die(error),
|
|
15
|
-
),
|
|
16
|
-
);
|
|
7
|
+
try: (signal) => Promise.resolve(evaluate(signal)),
|
|
8
|
+
}).pipe(Effect.catch((error) => (isPrismaFailure(error) ? Effect.fail(toPrismaError(error)) : Effect.die(error))));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { type PrismaError, toPrismaError } from "../error.
|
|
3
|
-
import type { DatabaseExecutor } from "./executor.
|
|
2
|
+
import { type PrismaError, toPrismaError } from "../error.ts";
|
|
3
|
+
import type { DatabaseExecutor } from "./executor.ts";
|
|
4
4
|
|
|
5
5
|
export const executeQuery = <A, E, R>(
|
|
6
6
|
executor: Pick<DatabaseExecutor<object>, "liveness" | "querySemaphore">,
|
|
@@ -8,11 +8,7 @@ export const executeQuery = <A, E, R>(
|
|
|
8
8
|
): Effect.Effect<A, E | PrismaError, R> => {
|
|
9
9
|
const checked = Effect.suspend(
|
|
10
10
|
(): Effect.Effect<A, E | PrismaError, R> =>
|
|
11
|
-
executor.liveness.open
|
|
12
|
-
? effect
|
|
13
|
-
: Effect.fail(toPrismaError({ code: executor.liveness.closedCode })),
|
|
11
|
+
executor.liveness.open ? effect : Effect.fail(toPrismaError({ code: executor.liveness.closedCode })),
|
|
14
12
|
);
|
|
15
|
-
return executor.querySemaphore === undefined
|
|
16
|
-
? checked
|
|
17
|
-
: executor.querySemaphore.withPermit(Effect.uninterruptible(checked));
|
|
13
|
+
return executor.querySemaphore === undefined ? checked : executor.querySemaphore.withPermit(Effect.uninterruptible(checked));
|
|
18
14
|
};
|