@shivaedev/effect-prisma 0.6.3 → 0.6.4

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 (129) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/dist/database.d.ts +4 -4
  3. package/dist/database.d.ts.map +1 -1
  4. package/dist/database.js +12 -12
  5. package/dist/database.js.map +1 -1
  6. package/dist/error.d.ts +5 -5
  7. package/dist/error.d.ts.map +1 -1
  8. package/dist/error.js +1 -2
  9. package/dist/error.js.map +1 -1
  10. package/dist/index.d.ts +3 -3
  11. package/dist/internal/adapters/transaction-settlement.d.ts +10 -0
  12. package/dist/internal/adapters/transaction-settlement.d.ts.map +1 -0
  13. package/dist/internal/adapters/transaction-settlement.js +56 -0
  14. package/dist/internal/adapters/transaction-settlement.js.map +1 -0
  15. package/dist/internal/client-lifecycle.d.ts.map +1 -1
  16. package/dist/internal/client-lifecycle.js.map +1 -1
  17. package/dist/internal/contract-normalization.d.ts.map +1 -1
  18. package/dist/internal/contract-normalization.js +1 -3
  19. package/dist/internal/contract-normalization.js.map +1 -1
  20. package/dist/internal/database-facade.d.ts +24 -0
  21. package/dist/internal/database-facade.d.ts.map +1 -0
  22. package/dist/internal/database-facade.js +84 -0
  23. package/dist/internal/database-facade.js.map +1 -0
  24. package/dist/internal/database-factory.d.ts +4 -44
  25. package/dist/internal/database-factory.d.ts.map +1 -1
  26. package/dist/internal/database-factory.js +11 -84
  27. package/dist/internal/database-factory.js.map +1 -1
  28. package/dist/internal/database-types.d.ts +37 -0
  29. package/dist/internal/database-types.d.ts.map +1 -0
  30. package/dist/internal/database-types.js +2 -0
  31. package/dist/internal/database-types.js.map +1 -0
  32. package/dist/internal/dynamic.d.ts +5 -0
  33. package/dist/internal/dynamic.d.ts.map +1 -0
  34. package/dist/internal/dynamic.js +12 -0
  35. package/dist/internal/dynamic.js.map +1 -0
  36. package/dist/internal/executor.d.ts.map +1 -1
  37. package/dist/internal/promise.d.ts +1 -1
  38. package/dist/internal/promise.d.ts.map +1 -1
  39. package/dist/internal/promise.js +1 -3
  40. package/dist/internal/promise.js.map +1 -1
  41. package/dist/internal/query-execution.d.ts +2 -2
  42. package/dist/internal/query-execution.d.ts.map +1 -1
  43. package/dist/internal/query-execution.js +2 -6
  44. package/dist/internal/query-execution.js.map +1 -1
  45. package/dist/internal/recipe.d.ts.map +1 -1
  46. package/dist/internal/recipe.js +7 -18
  47. package/dist/internal/recipe.js.map +1 -1
  48. package/dist/internal/relation-plan.d.ts +1 -1
  49. package/dist/internal/relation-result.d.ts +4 -0
  50. package/dist/internal/relation-result.d.ts.map +1 -0
  51. package/dist/internal/relation-result.js +19 -0
  52. package/dist/internal/relation-result.js.map +1 -0
  53. package/dist/internal/relation-runtime.d.ts +3 -3
  54. package/dist/internal/relation-runtime.d.ts.map +1 -1
  55. package/dist/internal/relation-runtime.js +9 -34
  56. package/dist/internal/relation-runtime.js.map +1 -1
  57. package/dist/internal/relation-stream.d.ts +3 -3
  58. package/dist/internal/relation-stream.d.ts.map +1 -1
  59. package/dist/internal/relation-stream.js +12 -14
  60. package/dist/internal/relation-stream.js.map +1 -1
  61. package/dist/internal/sqlite-datetime.d.ts +1 -1
  62. package/dist/internal/sqlite-datetime.d.ts.map +1 -1
  63. package/dist/internal/sqlite-datetime.js +3 -1
  64. package/dist/internal/sqlite-datetime.js.map +1 -1
  65. package/dist/internal/sqlite-pragmas.d.ts.map +1 -1
  66. package/dist/internal/sqlite-pragmas.js.map +1 -1
  67. package/dist/internal/testing.d.ts +1 -1
  68. package/dist/internal/testing.d.ts.map +1 -1
  69. package/dist/internal/testing.js +5 -2
  70. package/dist/internal/testing.js.map +1 -1
  71. package/dist/internal/transaction.d.ts +5 -5
  72. package/dist/internal/transaction.d.ts.map +1 -1
  73. package/dist/internal/transaction.js +3 -51
  74. package/dist/internal/transaction.js.map +1 -1
  75. package/dist/relation/include-metadata.d.ts.map +1 -1
  76. package/dist/relation/include.d.ts +2 -2
  77. package/dist/relation/include.d.ts.map +1 -1
  78. package/dist/relation/prisma-methods.d.ts +3 -3
  79. package/dist/relation/prisma-methods.d.ts.map +1 -1
  80. package/dist/relation.d.ts +2 -2
  81. package/dist/relation.d.ts.map +1 -1
  82. package/dist/sqlite.d.ts +3 -3
  83. package/dist/sqlite.d.ts.map +1 -1
  84. package/dist/sqlite.js +14 -12
  85. package/dist/sqlite.js.map +1 -1
  86. package/dist/testing/transaction.d.ts +6 -7
  87. package/dist/testing/transaction.d.ts.map +1 -1
  88. package/dist/testing/transaction.js +2 -2
  89. package/dist/testing/transaction.js.map +1 -1
  90. package/dist/testing/types.d.ts +2 -2
  91. package/dist/testing/types.d.ts.map +1 -1
  92. package/dist/testing/vitest.d.ts +3 -3
  93. package/dist/testing/vitest.d.ts.map +1 -1
  94. package/dist/testing/vitest.js +2 -2
  95. package/dist/testing/vitest.js.map +1 -1
  96. package/dist/testing.d.ts +3 -3
  97. package/package.json +2 -2
  98. package/src/bin/normalize-contract.ts +1 -1
  99. package/src/database.ts +49 -67
  100. package/src/error.ts +9 -19
  101. package/src/index.ts +3 -3
  102. package/src/internal/adapters/transaction-settlement.ts +67 -0
  103. package/src/internal/client-lifecycle.ts +3 -10
  104. package/src/internal/contract-normalization.ts +3 -8
  105. package/src/internal/database-facade.ts +143 -0
  106. package/src/internal/database-factory.ts +30 -298
  107. package/src/internal/database-types.ts +69 -0
  108. package/src/internal/dynamic.ts +16 -0
  109. package/src/internal/executor.ts +1 -4
  110. package/src/internal/promise.ts +3 -11
  111. package/src/internal/query-execution.ts +4 -8
  112. package/src/internal/recipe.ts +19 -84
  113. package/src/internal/relation-plan.ts +1 -1
  114. package/src/internal/relation-result.ts +27 -0
  115. package/src/internal/relation-runtime.ts +43 -133
  116. package/src/internal/relation-stream.ts +22 -48
  117. package/src/internal/sqlite-datetime.ts +10 -23
  118. package/src/internal/sqlite-pragmas.ts +1 -4
  119. package/src/internal/testing.ts +13 -15
  120. package/src/internal/transaction.ts +14 -107
  121. package/src/relation/include-metadata.ts +10 -42
  122. package/src/relation/include.ts +19 -115
  123. package/src/relation/prisma-methods.ts +23 -111
  124. package/src/relation.ts +18 -46
  125. package/src/sqlite.ts +57 -78
  126. package/src/testing/transaction.ts +20 -33
  127. package/src/testing/types.ts +9 -28
  128. package/src/testing/vitest.ts +14 -28
  129. package/src/testing.ts +3 -3
package/src/database.ts CHANGED
@@ -1,26 +1,18 @@
1
- import postgres, {
2
- type PostgresClient,
3
- type PostgresOptionsBase,
4
- } from "@prisma-next/postgres/runtime";
1
+ import postgres, { type PostgresClient, type PostgresOptionsBase } from "@prisma-next/postgres/runtime";
5
2
  import { type Layer, Redacted } from "effect";
6
- import type { PrismaError } from "./error.js";
7
- import { acquireConnectedClient } from "./internal/client-lifecycle.js";
8
- import {
9
- type DatabaseIdentifier,
10
- type DatabaseIdentifierLiteral,
11
- type DatabaseServiceHolder,
12
- type DefaultModels,
13
- makeSqlDatabase,
14
- namespaceModels,
15
- } from "./internal/database-factory.js";
16
- import type { AnySqlContract } from "./internal/executor.js";
17
- import { fromPrismaPromise } from "./internal/promise.js";
3
+ import type { PrismaError } from "./error.ts";
4
+ import { acquireConnectedClient } from "./internal/client-lifecycle.ts";
5
+ import { namespaceModels } from "./internal/database-facade.ts";
6
+ import { makeSqlDatabase } from "./internal/database-factory.ts";
7
+ import type { DatabaseIdentifier, DatabaseIdentifierLiteral, DatabaseServiceHolder, DefaultModels } from "./internal/database-types.ts";
8
+ import type { AnySqlContract } from "./internal/executor.ts";
9
+ import { fromPrismaPromise } from "./internal/promise.ts";
18
10
 
19
11
  export type {
20
12
  AnyDatabase,
21
13
  DatabaseService,
22
14
  DatabaseServiceOf,
23
- } from "./internal/database-factory.js";
15
+ } from "./internal/database-types.ts";
24
16
 
25
17
  export interface DatabaseLayerOptions extends PostgresOptionsBase {
26
18
  readonly url: string | Redacted.Redacted<string>;
@@ -36,18 +28,20 @@ type DatabaseFactoryOptions<Contract extends AnySqlContract> =
36
28
  readonly contract?: never;
37
29
  };
38
30
 
39
- export interface DatabaseDefinition<
40
- Contract extends AnySqlContract,
41
- Identifier extends string,
42
- > extends DatabaseServiceHolder<Contract, Identifier> {
43
- readonly layer: (
44
- options: DatabaseLayerOptions,
45
- ) => Layer.Layer<DatabaseIdentifier<Contract, Identifier>, PrismaError>;
31
+ export interface DatabaseDefinition<Contract extends AnySqlContract, Identifier extends string> extends DatabaseServiceHolder<Contract, Identifier> {
32
+ readonly layer: (options: DatabaseLayerOptions) => Layer.Layer<DatabaseIdentifier<Contract, Identifier>, PrismaError>;
46
33
  }
47
34
 
48
- const defaultModels = <Contract extends AnySqlContract, Models extends object>(
49
- client: Pick<PostgresClient<Contract>, "contract" | "orm">,
50
- ): Models => namespaceModels<Contract, Models>(client.contract, client.orm);
35
+ const clientOptions = ({ url, extensions, middleware, poolOptions, verifyMarker }: DatabaseLayerOptions) => ({
36
+ url: typeof url === "string" ? url : Redacted.value(url),
37
+ ...(extensions === undefined ? {} : { extensions }),
38
+ ...(middleware === undefined ? {} : { middleware }),
39
+ ...(poolOptions === undefined ? {} : { poolOptions }),
40
+ ...(verifyMarker === undefined ? {} : { verifyMarker }),
41
+ });
42
+
43
+ const defaultModels = <Contract extends AnySqlContract, Models extends object>(client: Pick<PostgresClient<Contract>, "contract" | "orm">): Models =>
44
+ namespaceModels<Contract, Models>(client.contract, client.orm);
51
45
 
52
46
  export const makeDatabase =
53
47
  <const Contract extends AnySqlContract>() =>
@@ -57,45 +51,33 @@ export const makeDatabase =
57
51
  ): DatabaseDefinition<Contract, Identifier> => {
58
52
  type Models = DefaultModels<Contract>;
59
53
 
60
- return makeSqlDatabase<Contract, Identifier, DatabaseLayerOptions>(
61
- identifier,
62
- (layerOptions) => {
63
- const clientOptions = {
64
- url:
65
- typeof layerOptions.url === "string"
66
- ? layerOptions.url
67
- : Redacted.value(layerOptions.url),
68
- extensions: layerOptions.extensions,
69
- middleware: layerOptions.middleware,
70
- poolOptions: layerOptions.poolOptions,
71
- verifyMarker: layerOptions.verifyMarker,
72
- };
73
- const client =
74
- options.contract === undefined
75
- ? postgres<Contract>({
76
- ...clientOptions,
77
- contractJson: options.contractJson,
78
- })
79
- : postgres<Contract>({
80
- ...clientOptions,
81
- contract: options.contract,
82
- });
54
+ return makeSqlDatabase<Contract, Identifier, DatabaseLayerOptions>(identifier, (layerOptions) => {
55
+ const forwarded = clientOptions(layerOptions);
56
+ const client =
57
+ options.contract === undefined
58
+ ? postgres<Contract>({
59
+ ...forwarded,
60
+ contractJson: options.contractJson,
61
+ })
62
+ : postgres<Contract>({
63
+ ...forwarded,
64
+ contract: options.contract,
65
+ });
83
66
 
84
- return fromPrismaPromise(() =>
85
- acquireConnectedClient(client, () => ({
86
- client,
87
- identity: {},
88
- liveness: {
89
- closedCode: "RUNTIME.DATABASE_CLOSED",
90
- open: true,
91
- },
92
- mode: "root",
93
- models: defaultModels<Contract, Models>(client),
94
- querySemaphore: undefined,
95
- transactionIdentity: undefined,
96
- transactionSemaphore: undefined,
97
- })),
98
- );
99
- },
100
- ) as DatabaseDefinition<Contract, Identifier>;
67
+ return fromPrismaPromise(() =>
68
+ acquireConnectedClient(client, () => ({
69
+ client,
70
+ identity: {},
71
+ liveness: {
72
+ closedCode: "RUNTIME.DATABASE_CLOSED",
73
+ open: true,
74
+ },
75
+ mode: "root",
76
+ models: defaultModels<Contract, Models>(client),
77
+ querySemaphore: undefined,
78
+ transactionIdentity: undefined,
79
+ transactionSemaphore: undefined,
80
+ })),
81
+ );
82
+ });
101
83
  };
package/src/error.ts CHANGED
@@ -1,31 +1,24 @@
1
1
  import { Data, Redacted } from "effect";
2
2
 
3
- export class PrismaRuntimeFailure extends Data.TaggedClass(
4
- "PrismaRuntimeFailure",
5
- )<{
3
+ export class PrismaRuntimeFailure extends Data.TaggedClass("PrismaRuntimeFailure")<{
6
4
  readonly code: string;
7
5
  readonly original: Redacted.Redacted<unknown>;
8
6
  }> {}
9
7
 
10
8
  export class PrismaQueryFailure extends Data.TaggedClass("PrismaQueryFailure")<{
11
- readonly sqlState?: string;
12
- readonly constraint?: string;
13
- readonly table?: string;
14
- readonly column?: string;
9
+ readonly sqlState?: string | undefined;
10
+ readonly constraint?: string | undefined;
11
+ readonly table?: string | undefined;
12
+ readonly column?: string | undefined;
15
13
  readonly original: Redacted.Redacted<unknown>;
16
14
  }> {}
17
15
 
18
- export class PrismaConnectionFailure extends Data.TaggedClass(
19
- "PrismaConnectionFailure",
20
- )<{
21
- readonly transient?: boolean;
16
+ export class PrismaConnectionFailure extends Data.TaggedClass("PrismaConnectionFailure")<{
17
+ readonly transient?: boolean | undefined;
22
18
  readonly original: Redacted.Redacted<unknown>;
23
19
  }> {}
24
20
 
25
- export type PrismaErrorReason =
26
- | PrismaRuntimeFailure
27
- | PrismaQueryFailure
28
- | PrismaConnectionFailure;
21
+ export type PrismaErrorReason = PrismaRuntimeFailure | PrismaQueryFailure | PrismaConnectionFailure;
29
22
 
30
23
  export class PrismaError extends Data.TaggedError("PrismaError")<{
31
24
  readonly reason: PrismaErrorReason;
@@ -52,10 +45,7 @@ export const isPrismaFailure = (error: unknown): error is PrismaFailure => {
52
45
  if ("code" in error && typeof error.code === "string") {
53
46
  return true;
54
47
  }
55
- return (
56
- "kind" in error &&
57
- (error.kind === "sql_query" || error.kind === "sql_connection")
58
- );
48
+ return "kind" in error && (error.kind === "sql_query" || error.kind === "sql_connection");
59
49
  };
60
50
 
61
51
  export const toPrismaError = (error: PrismaFailure): PrismaError => {
package/src/index.ts CHANGED
@@ -6,12 +6,12 @@ export {
6
6
  type DatabaseService,
7
7
  type DatabaseServiceOf,
8
8
  makeDatabase,
9
- } from "./database.js";
9
+ } from "./database.ts";
10
10
  export {
11
11
  PrismaConnectionFailure,
12
12
  PrismaError,
13
13
  type PrismaErrorReason,
14
14
  PrismaQueryFailure,
15
15
  PrismaRuntimeFailure,
16
- } from "./error.js";
17
- export type { Relation } from "./relation.js";
16
+ } from "./error.ts";
17
+ export type { Relation } from "./relation.ts";
@@ -0,0 +1,67 @@
1
+ import type { RuntimeConnection, RuntimeTransaction } from "@prisma-next/sql-runtime";
2
+
3
+ export type SettledConnection = Pick<RuntimeConnection, "destroy" | "release">;
4
+ export type SettledTransaction = Pick<RuntimeTransaction, "commit" | "rollback">;
5
+
6
+ interface Settled {
7
+ readonly connection: SettledConnection;
8
+ readonly transaction: SettledTransaction;
9
+ }
10
+
11
+ const runtimeFailure = (code: string, cause: unknown, details?: Readonly<Record<string, unknown>>): Error & { readonly code: string } =>
12
+ Object.assign(new Error(code, { cause }), { code, ...details });
13
+
14
+ class ConnectionDisposal {
15
+ disposed = false;
16
+
17
+ constructor(private readonly connection: SettledConnection) {}
18
+
19
+ async destroy(reason: unknown): Promise<void> {
20
+ if (this.disposed) {
21
+ return;
22
+ }
23
+ this.disposed = true;
24
+ await this.connection.destroy(reason).catch(() => undefined);
25
+ }
26
+ }
27
+
28
+ const commit = async ({ transaction }: Settled, disposal: ConnectionDisposal): Promise<unknown> => {
29
+ try {
30
+ await transaction.commit();
31
+ return undefined;
32
+ } catch (commitError) {
33
+ await transaction.rollback().catch(() => disposal.destroy(commitError));
34
+ return runtimeFailure("RUNTIME.TRANSACTION_COMMIT_FAILED", commitError);
35
+ }
36
+ };
37
+
38
+ const rollback = async ({ transaction }: Settled, disposal: ConnectionDisposal): Promise<unknown> => {
39
+ try {
40
+ await transaction.rollback();
41
+ return undefined;
42
+ } catch (rollbackError) {
43
+ await disposal.destroy(rollbackError);
44
+ return runtimeFailure("RUNTIME.TRANSACTION_ROLLBACK_FAILED", rollbackError);
45
+ }
46
+ };
47
+
48
+ const release = async ({ connection }: Settled, disposal: ConnectionDisposal, failure: unknown): Promise<void> => {
49
+ if (disposal.disposed) {
50
+ return;
51
+ }
52
+ try {
53
+ await connection.release();
54
+ } catch (releaseError) {
55
+ await disposal.destroy(releaseError);
56
+ throw failure === undefined ? releaseError : runtimeFailure("RUNTIME.TRANSACTION_RELEASE_FAILED", failure, { releaseError });
57
+ }
58
+ };
59
+
60
+ export const settleConnection = async (settled: Settled, commitTransaction: boolean): Promise<void> => {
61
+ const disposal = new ConnectionDisposal(settled.connection);
62
+ const failure = commitTransaction ? await commit(settled, disposal) : await rollback(settled, disposal);
63
+ await release(settled, disposal, failure);
64
+ if (failure !== undefined) {
65
+ throw failure;
66
+ }
67
+ };
@@ -3,10 +3,7 @@ export interface ClientLifecycle {
3
3
  readonly close: () => PromiseLike<void>;
4
4
  }
5
5
 
6
- export const acquireConnectedClient = async <A>(
7
- client: ClientLifecycle,
8
- initialize: () => A,
9
- ): Promise<A> => {
6
+ export const acquireConnectedClient = async <A>(client: ClientLifecycle, initialize: () => A): Promise<A> => {
10
7
  try {
11
8
  await client.connect();
12
9
  return initialize();
@@ -18,14 +15,10 @@ export const acquireConnectedClient = async <A>(
18
15
 
19
16
  const reservedModelNames = new Set(["transaction"]);
20
17
 
21
- export const assertAvailableModelNames = (
22
- modelNames: ReadonlyArray<string>,
23
- ): void => {
18
+ export const assertAvailableModelNames = (modelNames: ReadonlyArray<string>): void => {
24
19
  for (const modelName of modelNames) {
25
20
  if (reservedModelNames.has(modelName)) {
26
- throw new TypeError(
27
- `Prisma model name conflicts with the database facade: ${modelName}`,
28
- );
21
+ throw new TypeError(`Prisma model name conflicts with the database facade: ${modelName}`);
29
22
  }
30
23
  }
31
24
  };
@@ -1,6 +1,5 @@
1
1
  const timestampReference = /\b(?:Timestamp|Timestamptz)<(?:\d+|undefined)>/g;
2
- const timestampCodecReference =
3
- /\bCodecTypes\['pg\/(?:timestamp|timestamptz)@1'\]\['(?:input|output)'\]/g;
2
+ const timestampCodecReference = /\bCodecTypes\['pg\/(?:timestamp|timestamptz)@1'\]\['(?:input|output)'\]/g;
4
3
  const unsupportedTimestampReference = /\b(?:Timestamp|Timestamptz)\s*</;
5
4
 
6
5
  /**
@@ -8,14 +7,10 @@ const unsupportedTimestampReference = /\b(?:Timestamp|Timestamptz)\s*</;
8
7
  * JavaScript Dates accepted and returned by its runtime codecs.
9
8
  */
10
9
  export const normalizePrismaNextContractTypes = (source: string): string => {
11
- const normalized = source
12
- .replaceAll(timestampReference, "Date")
13
- .replaceAll(timestampCodecReference, "Date");
10
+ const normalized = source.replaceAll(timestampReference, "Date").replaceAll(timestampCodecReference, "Date");
14
11
 
15
12
  if (unsupportedTimestampReference.test(normalized)) {
16
- throw new Error(
17
- "Unsupported Prisma Next timestamp declaration; update effect-prisma before using this generated contract",
18
- );
13
+ throw new Error("Unsupported Prisma Next timestamp declaration; update effect-prisma before using this generated contract");
19
14
  }
20
15
 
21
16
  return normalized;
@@ -0,0 +1,143 @@
1
+ import { type Context, Effect } from "effect";
2
+ import { type PrismaError, toPrismaError } from "../error.ts";
3
+ import { assertAvailableModelNames } from "./client-lifecycle.ts";
4
+ import type { DatabaseIdentifier, DatabaseService, DefaultModels } from "./database-types.ts";
5
+ import type { AnySqlContract, DatabaseExecutor } from "./executor.ts";
6
+ import { makeModelRelation } from "./relation-runtime.ts";
7
+ import { acquireTransaction, releaseTransaction, type TransactionOrm, withTransactionSemaphore } from "./transaction.ts";
8
+
9
+ export interface ActiveTransaction<Contract extends AnySqlContract, Identifier extends string> {
10
+ readonly executor: DatabaseExecutor<DefaultModels<Contract>, Contract>;
11
+ readonly facade: DatabaseService<Contract, Identifier>;
12
+ }
13
+
14
+ export interface FacadeScope<Contract extends AnySqlContract, Identifier extends string> {
15
+ readonly activeTransaction: Context.Reference<ActiveTransaction<Contract, Identifier> | undefined>;
16
+ readonly executors: WeakMap<DatabaseService<Contract, Identifier>, DatabaseExecutor<DefaultModels<Contract>, Contract>>;
17
+ readonly service: Context.Service<DatabaseIdentifier<Contract, Identifier>, DatabaseService<Contract, Identifier>>;
18
+ }
19
+
20
+ // Prisma Next builds each namespace's model clients from the contract, so they are the contract's DefaultModels.
21
+ export function contractModels<Models extends object>(namespace: unknown): Models;
22
+ export function contractModels(namespace: unknown): unknown {
23
+ return namespace;
24
+ }
25
+
26
+ // The facade serves `transaction` itself and a Relation for every other model name, matching DatabaseService.
27
+ function databaseFacade<Contract extends AnySqlContract, Identifier extends string>(facade: object): DatabaseService<Contract, Identifier>;
28
+ function databaseFacade(facade: object): unknown {
29
+ return facade;
30
+ }
31
+
32
+ /**
33
+ * Resolve the models of the single domain namespace from a namespaced ORM
34
+ * client. Prisma Next namespaces PostgreSQL models under their schema and
35
+ * SQLite models under the unbound namespace.
36
+ */
37
+ export const namespaceModels = <Contract extends AnySqlContract, Models extends object>(
38
+ contract: Contract,
39
+ namespacedOrm: TransactionOrm<Contract>,
40
+ ): Models => {
41
+ const namespaces = Object.entries(contract.domain.namespaces);
42
+ const [only] = namespaces;
43
+ if (namespaces.length !== 1 || only === undefined) {
44
+ throw new TypeError("Effect Prisma currently requires exactly one domain namespace");
45
+ }
46
+ const [name, namespace] = only;
47
+ assertAvailableModelNames(Object.keys(namespace.models));
48
+ return contractModels<Models>(Reflect.get(namespacedOrm, name));
49
+ };
50
+
51
+ export const runTransaction = <Contract extends AnySqlContract, Identifier extends string, A, E, R>(
52
+ scope: FacadeScope<Contract, Identifier>,
53
+ current: DatabaseExecutor<DefaultModels<Contract>, Contract>,
54
+ currentFacade: DatabaseService<Contract, Identifier>,
55
+ program: Effect.Effect<A, E, R>,
56
+ release: typeof releaseTransaction,
57
+ mode: "test" | "transaction",
58
+ span: string,
59
+ ): Effect.Effect<A, E | PrismaError, Exclude<R, DatabaseIdentifier<Contract, Identifier>>> => {
60
+ if (!current.liveness.open) {
61
+ return Effect.fail(toPrismaError({ code: current.liveness.closedCode }));
62
+ }
63
+ if (current.mode === "test" || (current.mode === "transaction" && mode === "transaction")) {
64
+ return Effect.provideService(program, scope.service, currentFacade);
65
+ }
66
+ if (current.mode === "transaction") {
67
+ return Effect.fail(
68
+ toPrismaError({
69
+ code: "RUNTIME.TEST_TRANSACTION_INSIDE_TRANSACTION_UNSUPPORTED",
70
+ }),
71
+ );
72
+ }
73
+
74
+ const transaction = Effect.acquireUseRelease(
75
+ acquireTransaction(
76
+ current,
77
+ (transactionOrm) => namespaceModels<Contract, DefaultModels<Contract>>(current.client.contract, transactionOrm),
78
+ mode,
79
+ ),
80
+ (resource) => {
81
+ const transactionFacade = makeFacade(scope, resource.executor);
82
+ return program.pipe(
83
+ Effect.provideService(scope.service, transactionFacade),
84
+ Effect.provideService(scope.activeTransaction, {
85
+ executor: resource.executor,
86
+ facade: transactionFacade,
87
+ }),
88
+ );
89
+ },
90
+ release,
91
+ ).pipe(Effect.withSpan(span, { kind: "client" }));
92
+
93
+ return withTransactionSemaphore(current.transactionSemaphore, transaction);
94
+ };
95
+
96
+ export function makeFacade<Contract extends AnySqlContract, Identifier extends string>(
97
+ scope: FacadeScope<Contract, Identifier>,
98
+ current: DatabaseExecutor<DefaultModels<Contract>, Contract>,
99
+ ): DatabaseService<Contract, Identifier> {
100
+ let facade: DatabaseService<Contract, Identifier>;
101
+ const resolveActiveTransaction = Effect.flatMap(scope.activeTransaction, (active) => {
102
+ if (current.mode === "root") {
103
+ return Effect.succeed(active ?? { executor: current, facade });
104
+ }
105
+ return active?.executor === current
106
+ ? Effect.succeed(active)
107
+ : Effect.fail(
108
+ toPrismaError({
109
+ code: "RUNTIME.TRANSACTION_CONTEXT_MISMATCH",
110
+ }),
111
+ );
112
+ });
113
+ const resolveExecutor = Effect.suspend(() =>
114
+ current.liveness.open ? Effect.map(resolveActiveTransaction, ({ executor }) => executor) : Effect.succeed(current),
115
+ );
116
+ const target: object = Object.create(null);
117
+ Object.assign(target, {
118
+ transaction: <A, E, R>(program: Effect.Effect<A, E, R> & (DatabaseIdentifier<Contract, Identifier> extends R ? unknown : never)) =>
119
+ Effect.suspend(() => {
120
+ if (!current.liveness.open) {
121
+ return Effect.fail(toPrismaError({ code: current.liveness.closedCode }));
122
+ }
123
+ return Effect.flatMap(resolveActiveTransaction, (selected) =>
124
+ runTransaction(scope, selected.executor, selected.facade, program, releaseTransaction, "transaction", "prisma.transaction"),
125
+ );
126
+ }),
127
+ });
128
+ facade = databaseFacade<Contract, Identifier>(
129
+ new Proxy(target, {
130
+ get(target, property, receiver) {
131
+ if (Reflect.has(target, property)) {
132
+ return Reflect.get(target, property, receiver);
133
+ }
134
+ if (typeof property !== "string") {
135
+ return undefined;
136
+ }
137
+ return makeModelRelation(current, property, resolveExecutor);
138
+ },
139
+ }),
140
+ );
141
+ scope.executors.set(facade, current);
142
+ return facade;
143
+ }