@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.
Files changed (138) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/dist/bin/normalize-contract.js +1 -0
  3. package/dist/bin/normalize-contract.js.map +1 -1
  4. package/dist/database.d.ts +4 -4
  5. package/dist/database.d.ts.map +1 -1
  6. package/dist/database.js +12 -12
  7. package/dist/database.js.map +1 -1
  8. package/dist/error.d.ts +5 -5
  9. package/dist/error.d.ts.map +1 -1
  10. package/dist/error.js +5 -6
  11. package/dist/error.js.map +1 -1
  12. package/dist/index.d.ts +3 -3
  13. package/dist/internal/adapters/transaction-settlement.d.ts +10 -0
  14. package/dist/internal/adapters/transaction-settlement.d.ts.map +1 -0
  15. package/dist/internal/adapters/transaction-settlement.js +56 -0
  16. package/dist/internal/adapters/transaction-settlement.js.map +1 -0
  17. package/dist/internal/client-lifecycle.d.ts +2 -2
  18. package/dist/internal/client-lifecycle.d.ts.map +1 -1
  19. package/dist/internal/client-lifecycle.js.map +1 -1
  20. package/dist/internal/contract-normalization.d.ts +0 -4
  21. package/dist/internal/contract-normalization.d.ts.map +1 -1
  22. package/dist/internal/contract-normalization.js +5 -10
  23. package/dist/internal/contract-normalization.js.map +1 -1
  24. package/dist/internal/database-facade.d.ts +19 -0
  25. package/dist/internal/database-facade.d.ts.map +1 -0
  26. package/dist/internal/database-facade.js +79 -0
  27. package/dist/internal/database-facade.js.map +1 -0
  28. package/dist/internal/database-factory.d.ts +4 -48
  29. package/dist/internal/database-factory.d.ts.map +1 -1
  30. package/dist/internal/database-factory.js +11 -90
  31. package/dist/internal/database-factory.js.map +1 -1
  32. package/dist/internal/database-types.d.ts +33 -0
  33. package/dist/internal/database-types.d.ts.map +1 -0
  34. package/dist/internal/database-types.js +2 -0
  35. package/dist/internal/database-types.js.map +1 -0
  36. package/dist/internal/dynamic.d.ts +5 -0
  37. package/dist/internal/dynamic.d.ts.map +1 -0
  38. package/dist/internal/dynamic.js +12 -0
  39. package/dist/internal/dynamic.js.map +1 -0
  40. package/dist/internal/executor.d.ts +3 -7
  41. package/dist/internal/executor.d.ts.map +1 -1
  42. package/dist/internal/promise.d.ts +1 -1
  43. package/dist/internal/promise.d.ts.map +1 -1
  44. package/dist/internal/promise.js +2 -4
  45. package/dist/internal/promise.js.map +1 -1
  46. package/dist/internal/query-execution.d.ts +2 -2
  47. package/dist/internal/query-execution.d.ts.map +1 -1
  48. package/dist/internal/query-execution.js +2 -6
  49. package/dist/internal/query-execution.js.map +1 -1
  50. package/dist/internal/recipe.d.ts +3 -3
  51. package/dist/internal/recipe.d.ts.map +1 -1
  52. package/dist/internal/recipe.js +9 -20
  53. package/dist/internal/recipe.js.map +1 -1
  54. package/dist/internal/relation-plan.d.ts +1 -1
  55. package/dist/internal/relation-result.d.ts +4 -0
  56. package/dist/internal/relation-result.d.ts.map +1 -0
  57. package/dist/internal/relation-result.js +19 -0
  58. package/dist/internal/relation-result.js.map +1 -0
  59. package/dist/internal/relation-runtime.d.ts +3 -3
  60. package/dist/internal/relation-runtime.d.ts.map +1 -1
  61. package/dist/internal/relation-runtime.js +12 -37
  62. package/dist/internal/relation-runtime.js.map +1 -1
  63. package/dist/internal/relation-stream.d.ts +3 -3
  64. package/dist/internal/relation-stream.d.ts.map +1 -1
  65. package/dist/internal/relation-stream.js +20 -24
  66. package/dist/internal/relation-stream.js.map +1 -1
  67. package/dist/internal/sqlite-datetime.d.ts +1 -27
  68. package/dist/internal/sqlite-datetime.d.ts.map +1 -1
  69. package/dist/internal/sqlite-datetime.js +16 -35
  70. package/dist/internal/sqlite-datetime.js.map +1 -1
  71. package/dist/internal/sqlite-pragmas.d.ts +1 -13
  72. package/dist/internal/sqlite-pragmas.d.ts.map +1 -1
  73. package/dist/internal/sqlite-pragmas.js +2 -12
  74. package/dist/internal/sqlite-pragmas.js.map +1 -1
  75. package/dist/internal/testing.d.ts +1 -1
  76. package/dist/internal/testing.d.ts.map +1 -1
  77. package/dist/internal/testing.js +5 -2
  78. package/dist/internal/testing.js.map +1 -1
  79. package/dist/internal/transaction.d.ts +5 -5
  80. package/dist/internal/transaction.d.ts.map +1 -1
  81. package/dist/internal/transaction.js +5 -53
  82. package/dist/internal/transaction.js.map +1 -1
  83. package/dist/relation/include-metadata.d.ts +1 -1
  84. package/dist/relation/include-metadata.d.ts.map +1 -1
  85. package/dist/relation/include.d.ts +8 -5
  86. package/dist/relation/include.d.ts.map +1 -1
  87. package/dist/relation/prisma-methods.d.ts +17 -17
  88. package/dist/relation/prisma-methods.d.ts.map +1 -1
  89. package/dist/relation.d.ts +4 -4
  90. package/dist/relation.d.ts.map +1 -1
  91. package/dist/sqlite.d.ts +5 -10
  92. package/dist/sqlite.d.ts.map +1 -1
  93. package/dist/sqlite.js +14 -12
  94. package/dist/sqlite.js.map +1 -1
  95. package/dist/testing/transaction.d.ts +6 -7
  96. package/dist/testing/transaction.d.ts.map +1 -1
  97. package/dist/testing/transaction.js +2 -2
  98. package/dist/testing/transaction.js.map +1 -1
  99. package/dist/testing/types.d.ts +7 -7
  100. package/dist/testing/types.d.ts.map +1 -1
  101. package/dist/testing/vitest.d.ts +3 -3
  102. package/dist/testing/vitest.d.ts.map +1 -1
  103. package/dist/testing/vitest.js +2 -2
  104. package/dist/testing/vitest.js.map +1 -1
  105. package/dist/testing.d.ts +3 -3
  106. package/package.json +35 -37
  107. package/src/bin/normalize-contract.ts +2 -1
  108. package/src/database.ts +49 -67
  109. package/src/error.ts +13 -23
  110. package/src/index.ts +3 -3
  111. package/src/internal/adapters/transaction-settlement.ts +70 -0
  112. package/src/internal/client-lifecycle.ts +4 -11
  113. package/src/internal/contract-normalization.ts +6 -14
  114. package/src/internal/database-facade.ts +138 -0
  115. package/src/internal/database-factory.ts +34 -306
  116. package/src/internal/database-types.ts +65 -0
  117. package/src/internal/dynamic.ts +16 -0
  118. package/src/internal/executor.ts +4 -11
  119. package/src/internal/promise.ts +4 -12
  120. package/src/internal/query-execution.ts +4 -8
  121. package/src/internal/recipe.ts +24 -89
  122. package/src/internal/relation-plan.ts +1 -1
  123. package/src/internal/relation-result.ts +27 -0
  124. package/src/internal/relation-runtime.ts +45 -135
  125. package/src/internal/relation-stream.ts +24 -51
  126. package/src/internal/sqlite-datetime.ts +20 -54
  127. package/src/internal/sqlite-pragmas.ts +3 -16
  128. package/src/internal/testing.ts +13 -15
  129. package/src/internal/transaction.ts +16 -109
  130. package/src/relation/include-metadata.ts +11 -43
  131. package/src/relation/include.ts +46 -140
  132. package/src/relation/prisma-methods.ts +36 -122
  133. package/src/relation.ts +20 -48
  134. package/src/sqlite.ts +59 -85
  135. package/src/testing/transaction.ts +20 -33
  136. package/src/testing/types.ts +15 -34
  137. package/src/testing/vitest.ts +14 -28
  138. 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 { type PrismaError, toPrismaError } from "../error.js";
4
- import type { Relation } from "../relation.js";
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
- acquireTransaction,
11
- releaseTestTransaction,
12
- releaseTransaction,
13
- type TransactionOrm,
14
- withTransactionSemaphore,
15
- } from "./transaction.js";
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 type DatabaseIdentifierLiteral<Identifier extends string> = [
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
- DatabaseId,
168
- DatabaseService<Contract, Identifier>
169
- >(identifier);
170
- const executors = new WeakMap<
171
- DatabaseService<Contract, Identifier>,
172
- DatabaseExecutor<Models, Contract>
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 ActiveTransaction = Context.Reference<ActiveTransaction | undefined>(
179
- `${internalContextIdentifierPrefix}ActiveTransaction/${activeTransactionReferenceSequence++}`,
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
- return Effect.flatMap(executorOf(facade), (current) =>
312
- runTransaction(
313
- current,
314
- facade,
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
- }) as SqlDatabase<Contract, Identifier, Options>;
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);
@@ -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
- readonly contract: Contract;
10
+ close: () => Promise<void>;
15
11
  readonly context: ExecutionContext<Contract>;
16
- runtime(): Runtime;
17
- close(): Promise<void>;
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: {
@@ -1,16 +1,8 @@
1
1
  import { Effect } from "effect";
2
- import { isPrismaFailure, type PrismaError, toPrismaError } from "../error.js";
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
- }).pipe(
11
- Effect.catch((error) =>
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.js";
3
- import type { DatabaseExecutor } from "./executor.js";
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
  };