@shivaedev/effect-prisma 0.6.4 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/bin/normalize-contract.js +2 -1
  3. package/dist/bin/normalize-contract.js.map +1 -1
  4. package/dist/database.d.ts +3 -3
  5. package/dist/database.d.ts.map +1 -1
  6. package/dist/database.js +4 -4
  7. package/dist/database.js.map +1 -1
  8. package/dist/error.js +4 -4
  9. package/dist/error.js.map +1 -1
  10. package/dist/internal/adapters/transaction-settlement.d.ts.map +1 -1
  11. package/dist/internal/adapters/transaction-settlement.js +1 -1
  12. package/dist/internal/adapters/transaction-settlement.js.map +1 -1
  13. package/dist/internal/client-lifecycle.d.ts +2 -2
  14. package/dist/internal/client-lifecycle.d.ts.map +1 -1
  15. package/dist/internal/client-lifecycle.js.map +1 -1
  16. package/dist/internal/contract-normalization.d.ts +0 -4
  17. package/dist/internal/contract-normalization.d.ts.map +1 -1
  18. package/dist/internal/contract-normalization.js +4 -7
  19. package/dist/internal/contract-normalization.js.map +1 -1
  20. package/dist/internal/database-facade.d.ts +1 -6
  21. package/dist/internal/database-facade.d.ts.map +1 -1
  22. package/dist/internal/database-facade.js +3 -8
  23. package/dist/internal/database-facade.js.map +1 -1
  24. package/dist/internal/database-factory.d.ts +1 -5
  25. package/dist/internal/database-factory.d.ts.map +1 -1
  26. package/dist/internal/database-factory.js +1 -7
  27. package/dist/internal/database-factory.js.map +1 -1
  28. package/dist/internal/database-types.d.ts +4 -8
  29. package/dist/internal/database-types.d.ts.map +1 -1
  30. package/dist/internal/dynamic.d.ts +1 -1
  31. package/dist/internal/dynamic.d.ts.map +1 -1
  32. package/dist/internal/dynamic.js.map +1 -1
  33. package/dist/internal/executor.d.ts +3 -7
  34. package/dist/internal/executor.d.ts.map +1 -1
  35. package/dist/internal/promise.d.ts +1 -1
  36. package/dist/internal/promise.d.ts.map +1 -1
  37. package/dist/internal/promise.js +2 -2
  38. package/dist/internal/promise.js.map +1 -1
  39. package/dist/internal/query-execution.d.ts +1 -1
  40. package/dist/internal/query-execution.d.ts.map +1 -1
  41. package/dist/internal/query-execution.js +1 -1
  42. package/dist/internal/query-execution.js.map +1 -1
  43. package/dist/internal/recipe.d.ts +3 -3
  44. package/dist/internal/recipe.d.ts.map +1 -1
  45. package/dist/internal/recipe.js +2 -2
  46. package/dist/internal/recipe.js.map +1 -1
  47. package/dist/internal/relation-result.d.ts +1 -1
  48. package/dist/internal/relation-result.d.ts.map +1 -1
  49. package/dist/internal/relation-result.js.map +1 -1
  50. package/dist/internal/relation-runtime.d.ts +2 -2
  51. package/dist/internal/relation-runtime.d.ts.map +1 -1
  52. package/dist/internal/relation-runtime.js +3 -3
  53. package/dist/internal/relation-runtime.js.map +1 -1
  54. package/dist/internal/relation-stream.d.ts +1 -1
  55. package/dist/internal/relation-stream.d.ts.map +1 -1
  56. package/dist/internal/relation-stream.js +13 -15
  57. package/dist/internal/relation-stream.js.map +1 -1
  58. package/dist/internal/sqlite-datetime.d.ts +0 -26
  59. package/dist/internal/sqlite-datetime.d.ts.map +1 -1
  60. package/dist/internal/sqlite-datetime.js +13 -34
  61. package/dist/internal/sqlite-datetime.js.map +1 -1
  62. package/dist/internal/sqlite-pragmas.d.ts +1 -13
  63. package/dist/internal/sqlite-pragmas.d.ts.map +1 -1
  64. package/dist/internal/sqlite-pragmas.js +2 -12
  65. package/dist/internal/sqlite-pragmas.js.map +1 -1
  66. package/dist/internal/testing.d.ts +1 -1
  67. package/dist/internal/testing.d.ts.map +1 -1
  68. package/dist/internal/transaction.d.ts +3 -3
  69. package/dist/internal/transaction.d.ts.map +1 -1
  70. package/dist/internal/transaction.js +3 -3
  71. package/dist/internal/transaction.js.map +1 -1
  72. package/dist/relation/include-metadata.d.ts +1 -1
  73. package/dist/relation/include-metadata.d.ts.map +1 -1
  74. package/dist/relation/include.d.ts +7 -4
  75. package/dist/relation/include.d.ts.map +1 -1
  76. package/dist/relation/prisma-methods.d.ts +16 -16
  77. package/dist/relation/prisma-methods.d.ts.map +1 -1
  78. package/dist/relation.d.ts +3 -3
  79. package/dist/relation.d.ts.map +1 -1
  80. package/dist/sqlite.d.ts +4 -9
  81. package/dist/sqlite.d.ts.map +1 -1
  82. package/dist/sqlite.js +6 -6
  83. package/dist/sqlite.js.map +1 -1
  84. package/dist/testing/transaction.d.ts +1 -1
  85. package/dist/testing/transaction.d.ts.map +1 -1
  86. package/dist/testing/transaction.js +1 -1
  87. package/dist/testing/transaction.js.map +1 -1
  88. package/dist/testing/types.d.ts +7 -7
  89. package/dist/testing/types.d.ts.map +1 -1
  90. package/dist/testing/vitest.d.ts.map +1 -1
  91. package/dist/testing/vitest.js.map +1 -1
  92. package/dist/testing.d.ts +3 -3
  93. package/dist/testing.d.ts.map +1 -1
  94. package/dist/testing.js +2 -2
  95. package/dist/testing.js.map +1 -1
  96. package/package.json +42 -36
  97. package/src/bin/normalize-contract.ts +2 -1
  98. package/src/database.ts +7 -7
  99. package/src/error.ts +4 -4
  100. package/src/internal/adapters/transaction-settlement.ts +4 -1
  101. package/src/internal/client-lifecycle.ts +2 -2
  102. package/src/internal/contract-normalization.ts +4 -7
  103. package/src/internal/database-facade.ts +3 -8
  104. package/src/internal/database-factory.ts +5 -9
  105. package/src/internal/database-types.ts +5 -9
  106. package/src/internal/dynamic.ts +1 -1
  107. package/src/internal/executor.ts +3 -7
  108. package/src/internal/promise.ts +2 -2
  109. package/src/internal/query-execution.ts +1 -1
  110. package/src/internal/recipe.ts +8 -8
  111. package/src/internal/relation-result.ts +2 -2
  112. package/src/internal/relation-runtime.ts +6 -6
  113. package/src/internal/relation-stream.ts +5 -6
  114. package/src/internal/sqlite-datetime.ts +13 -34
  115. package/src/internal/sqlite-pragmas.ts +3 -13
  116. package/src/internal/testing.ts +1 -1
  117. package/src/internal/transaction.ts +5 -5
  118. package/src/relation/include-metadata.ts +1 -1
  119. package/src/relation/include.ts +36 -34
  120. package/src/relation/prisma-methods.ts +21 -19
  121. package/src/relation.ts +3 -3
  122. package/src/sqlite.ts +10 -15
  123. package/src/testing/transaction.ts +2 -2
  124. package/src/testing/types.ts +8 -8
  125. package/src/testing/vitest.ts +1 -1
  126. package/src/testing.ts +3 -3
@@ -1,5 +1,5 @@
1
1
  import { Context, Effect, Layer } from "effect";
2
- import type { PrismaError } from "../error.ts";
2
+ import type { PrismaError } from "#error.ts";
3
3
  import { type ActiveTransaction, type FacadeScope, makeFacade, runTransaction } from "./database-facade.ts";
4
4
  import {
5
5
  type DatabaseIdentifier,
@@ -15,10 +15,6 @@ import { releaseTestTransaction } from "./transaction.ts";
15
15
 
16
16
  let activeTransactionReferenceSequence = 0;
17
17
 
18
- /**
19
- * Assemble the database service and its Layer around a driver-specific
20
- * executor acquisition.
21
- */
22
18
  export const makeSqlDatabase = <const Contract extends AnySqlContract, const Identifier extends string, Options>(
23
19
  identifier: DatabaseIdentifierLiteral<Identifier>,
24
20
  acquireExecutor: (options: Options) => Effect.Effect<DatabaseExecutor<DefaultModels<Contract>, Contract>, PrismaError>,
@@ -48,11 +44,11 @@ export const makeSqlDatabase = <const Contract extends AnySqlContract, const Ide
48
44
  const withTestTransaction = <A, E, R>(
49
45
  program: Effect.Effect<A, E, R> & (DatabaseId extends R ? unknown : never),
50
46
  ): Effect.Effect<A, E | PrismaError, DatabaseId | Exclude<R, DatabaseId>> =>
51
- Effect.flatMap(Service, (facade) => {
52
- return Effect.flatMap(executorOf(facade), (current) =>
47
+ Effect.flatMap(Service, (facade) =>
48
+ Effect.flatMap(executorOf(facade), (current) =>
53
49
  runTransaction(scope, current, facade, program, releaseTestTransaction, "test", "prisma.testTransaction"),
54
- );
55
- });
50
+ ),
51
+ );
56
52
 
57
53
  const layer = (layerOptions: Options): Layer.Layer<DatabaseId, PrismaError> => {
58
54
  const acquire = Effect.acquireRelease(
@@ -1,7 +1,7 @@
1
1
  import type { orm } from "@prisma-next/sql-orm-client";
2
2
  import type { Context, Effect, Layer } from "effect";
3
- import type { PrismaError } from "../error.ts";
4
- import type { Relation } from "../relation.ts";
3
+ import type { PrismaError } from "#error.ts";
4
+ import type { Relation } from "#relation.ts";
5
5
  import type { AnySqlContract } from "./executor.ts";
6
6
 
7
7
  type OrmFor<Contract extends AnySqlContract> = ReturnType<typeof orm<Contract>>;
@@ -45,20 +45,16 @@ type DatabaseModels<Contract extends AnySqlContract, Identifier extends string>
45
45
  };
46
46
 
47
47
  export type DatabaseService<Contract extends AnySqlContract, Identifier extends string> = DatabaseModels<Contract, Identifier> & {
48
- transaction<A, E, R>(
48
+ transaction: <A, E, R>(
49
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>>>;
50
+ ) => Effect.Effect<A, E | PrismaError, Exclude<R, DatabaseIdentifier<Contract, Identifier>>>;
51
51
  };
52
52
 
53
- /**
54
- * The driver-independent part of a database definition: the Context service
55
- * carrying the typed facade. Driver entrypoints add their own `layer` options.
56
- */
57
53
  export interface DatabaseServiceHolder<Contract extends AnySqlContract, Identifier extends string>
58
54
  extends Context.Service<DatabaseIdentifier<Contract, Identifier>, DatabaseService<Contract, Identifier>> {}
59
55
 
60
56
  export type AnyDatabase = Effect.Effect<unknown, never, unknown> & {
61
- readonly layer: (...arguments_: ReadonlyArray<never>) => Layer.Any;
57
+ readonly layer: (...arguments_: readonly never[]) => Layer.Any;
62
58
  };
63
59
 
64
60
  export type DatabaseServiceOf<Database extends AnyDatabase> = Effect.Success<Database>;
@@ -3,7 +3,7 @@ const isObjectLike = (value: unknown): value is object => (typeof value === "obj
3
3
  export const hasMethod = (value: unknown, name: PropertyKey): value is object =>
4
4
  isObjectLike(value) && typeof Reflect.get(value, name) === "function";
5
5
 
6
- export const invokeMethod = (target: object, name: PropertyKey, arguments_: ReadonlyArray<unknown>): unknown => {
6
+ export const invokeMethod = (target: object, name: PropertyKey, arguments_: readonly unknown[]): unknown => {
7
7
  const method: unknown = Reflect.get(target, name);
8
8
  if (typeof method !== "function") {
9
9
  throw new TypeError(`${String(name)} is not a method`);
@@ -6,15 +6,11 @@ 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
16
  export interface DatabaseExecutor<Models extends object, Contract extends AnySqlContract = AnySqlContract> {
@@ -1,8 +1,8 @@
1
1
  import { Effect } from "effect";
2
- import { isPrismaFailure, type PrismaError, toPrismaError } from "../error.ts";
2
+ import { isPrismaFailure, type PrismaError, toPrismaError } from "#error.ts";
3
3
 
4
4
  export const fromPrismaPromise = <A>(evaluate: (signal: AbortSignal) => PromiseLike<A>): Effect.Effect<A, PrismaError> =>
5
5
  Effect.tryPromise({
6
- try: (signal) => Promise.resolve(evaluate(signal)),
7
6
  catch: (error) => error,
7
+ try: (signal) => Promise.resolve(evaluate(signal)),
8
8
  }).pipe(Effect.catch((error) => (isPrismaFailure(error) ? Effect.fail(toPrismaError(error)) : Effect.die(error))));
@@ -1,5 +1,5 @@
1
1
  import { Effect } from "effect";
2
- import { type PrismaError, toPrismaError } from "../error.ts";
2
+ import { type PrismaError, toPrismaError } from "#error.ts";
3
3
  import type { DatabaseExecutor } from "./executor.ts";
4
4
 
5
5
  export const executeQuery = <A, E, R>(
@@ -2,29 +2,29 @@ import { hasMethod, invokeMethod } from "./dynamic.ts";
2
2
  import { getRelationPlan, type RelationPlan } from "./relation-plan.ts";
3
3
 
4
4
  export interface RelationOperation {
5
+ readonly arguments: readonly unknown[];
5
6
  readonly name: PropertyKey;
6
- readonly arguments: ReadonlyArray<unknown>;
7
7
  }
8
8
 
9
9
  export interface RelationRecipe {
10
10
  readonly model: string;
11
- readonly parent?: RelationRecipe;
12
11
  readonly operation?: RelationOperation;
12
+ readonly parent?: RelationRecipe;
13
13
  }
14
14
 
15
15
  export const rootRecipe = (model: string): RelationRecipe => ({ model });
16
16
 
17
- export const appendOperation = (parent: RelationRecipe, name: PropertyKey, arguments_: ReadonlyArray<unknown>): RelationRecipe => ({
17
+ export const appendOperation = (parent: RelationRecipe, name: PropertyKey, arguments_: readonly unknown[]): RelationRecipe => ({
18
18
  model: parent.model,
19
- parent,
20
19
  operation: {
21
- name,
22
20
  arguments: arguments_,
21
+ name,
23
22
  },
23
+ parent,
24
24
  });
25
25
 
26
- const operations = (recipe: RelationRecipe): ReadonlyArray<RelationOperation> => {
27
- const reversed: Array<RelationOperation> = [];
26
+ const operations = (recipe: RelationRecipe): readonly RelationOperation[] => {
27
+ const reversed: RelationOperation[] = [];
28
28
  let current: RelationRecipe | undefined = recipe;
29
29
 
30
30
  while (current !== undefined) {
@@ -37,7 +37,7 @@ const operations = (recipe: RelationRecipe): ReadonlyArray<RelationOperation> =>
37
37
  return reversed.reverse();
38
38
  };
39
39
 
40
- const applyMethod = (current: unknown, name: PropertyKey, arguments_: ReadonlyArray<unknown>, model: string): unknown => {
40
+ const applyMethod = (current: unknown, name: PropertyKey, arguments_: readonly unknown[], model: string): unknown => {
41
41
  if (!hasMethod(current, name)) {
42
42
  throw new TypeError(`Cannot call ${String(name)} while replaying ${model}`);
43
43
  }
@@ -1,5 +1,5 @@
1
1
  import { Effect, Option } from "effect";
2
- import type { PrismaError } from "../error.ts";
2
+ import type { PrismaError } from "#error.ts";
3
3
  import { hasMethod, invokeMethod, isPromiseLike } from "./dynamic.ts";
4
4
  import { fromPrismaPromise } from "./promise.ts";
5
5
 
@@ -10,7 +10,7 @@ const countOf = (result: unknown): unknown => (typeof result === "object" && res
10
10
 
11
11
  export const evaluateResult = (value: unknown, terminal: PropertyKey | undefined): Effect.Effect<unknown, PrismaError> => {
12
12
  if (terminal === "count" && hasMethod(value, "aggregate")) {
13
- return settle(invokeMethod(value, "aggregate", [(summary: { count(): unknown }) => ({ count: summary.count() })]), countOf);
13
+ return settle(invokeMethod(value, "aggregate", [(summary: { count: () => unknown }) => ({ count: summary.count() })]), countOf);
14
14
  }
15
15
 
16
16
  if (terminal === "exists" && hasMethod(value, "first")) {
@@ -1,6 +1,6 @@
1
1
  import { Effect, Effectable, type Stream } from "effect";
2
- import type { PrismaError } from "../error.ts";
3
- import type { Relation } from "../relation.ts";
2
+ import type { PrismaError } from "#error.ts";
3
+ import type { Relation } from "#relation.ts";
4
4
  import type { AnyPostgresContract, DatabaseExecutor } from "./executor.ts";
5
5
  import { executeQuery } from "./query-execution.ts";
6
6
  import { appendOperation, type RelationRecipe, replayRecipe, rootRecipe } from "./recipe.ts";
@@ -42,22 +42,22 @@ const relationEffect = (self: object): Effect.Effect<unknown, PrismaError> => {
42
42
  ),
43
43
  ).pipe(
44
44
  Effect.withSpan(`prisma.${runtime.recipe.model}.${String(runtime.terminal ?? "all")}`, {
45
- kind: "client",
46
45
  attributes: {
47
- "db.system": "postgresql",
48
46
  "db.model": runtime.recipe.model,
49
47
  "db.operation": String(runtime.terminal ?? "all"),
48
+ "db.system": "postgresql",
50
49
  },
50
+ kind: "client",
51
51
  }),
52
52
  );
53
53
  };
54
54
 
55
55
  const RelationPrototype = {
56
56
  ...Effectable.Prototype<RelationValue>({
57
- label: "EffectPrismaRelation",
58
57
  evaluate() {
59
58
  return relationEffect(this);
60
59
  },
60
+ label: "EffectPrismaRelation",
61
61
  }),
62
62
  get stream(): Stream.Stream<unknown, PrismaError> {
63
63
  const runtime = runtimeOf(this);
@@ -89,7 +89,7 @@ const makeRelationProxy = (runtime: AnyRelationRuntime): object => {
89
89
  terminal: "count",
90
90
  });
91
91
  }
92
- return (...arguments_: ReadonlyArray<unknown>) =>
92
+ return (...arguments_: readonly unknown[]) =>
93
93
  makeRelationProxy({
94
94
  ...runtime,
95
95
  recipe: appendOperation(runtime.recipe, property, arguments_),
@@ -1,14 +1,14 @@
1
1
  import { Effect, Stream } from "effect";
2
- import { isPrismaFailure, type PrismaError, toPrismaError } from "../error.ts";
2
+ import { isPrismaFailure, type PrismaError, toPrismaError } from "#error.ts";
3
3
  import { hasMethod, invokeMethod, isAsyncIterable } from "./dynamic.ts";
4
4
  import type { AnyPostgresContract, DatabaseExecutor } from "./executor.ts";
5
5
  import { fromPrismaPromise } from "./promise.ts";
6
6
  import { executeQuery } from "./query-execution.ts";
7
7
  import { type RelationRecipe, replayRecipe } from "./recipe.ts";
8
8
 
9
- const collectRows = (iterable: AsyncIterable<unknown>): Effect.Effect<Array<unknown>, PrismaError> =>
9
+ const collectRows = (iterable: AsyncIterable<unknown>): Effect.Effect<unknown[], PrismaError> =>
10
10
  fromPrismaPromise(async () => {
11
- const rows: Array<unknown> = [];
11
+ const rows: unknown[] = [];
12
12
  for await (const row of iterable) {
13
13
  rows.push(row);
14
14
  }
@@ -18,8 +18,8 @@ const collectRows = (iterable: AsyncIterable<unknown>): Effect.Effect<Array<unkn
18
18
  export const makeRelationStream = <Models extends object, Contract extends AnyPostgresContract>(
19
19
  resolveExecutor: Effect.Effect<DatabaseExecutor<Models, Contract>, PrismaError>,
20
20
  recipe: RelationRecipe,
21
- ): Stream.Stream<unknown, PrismaError> => {
22
- return Stream.unwrap(
21
+ ): Stream.Stream<unknown, PrismaError> =>
22
+ Stream.unwrap(
23
23
  Effect.flatMap(resolveExecutor, (executor) =>
24
24
  executeQuery(
25
25
  executor,
@@ -43,4 +43,3 @@ export const makeRelationStream = <Models extends object, Contract extends AnyPo
43
43
  ),
44
44
  ),
45
45
  );
46
- };
@@ -8,44 +8,23 @@ type CodecJson = Parameters<ContractCodec["decodeJson"]>[0];
8
8
 
9
9
  const sqliteDatetimeCodecId = "sqlite/datetime@1";
10
10
 
11
- const zonelessDatetime = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?)$/;
11
+ const zonelessDatetime = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?)$/u;
12
12
 
13
- /**
14
- * Stamp `Z` onto a SQLite datetime string that carries no zone designator.
15
- *
16
- * SQLite stores `DateTime` as text and computes `datetime('now')` — the default
17
- * `prisma-next db init` generates — in UTC, but writes it as
18
- * `YYYY-MM-DD HH:MM:SS` with no zone. `new Date` reads that form as local time,
19
- * so decoding it unchanged shifts the instant by the process' UTC offset.
20
- * Values that already carry `Z` or a numeric offset, date-only values (which
21
- * `new Date` already reads as UTC), and anything that is not a datetime string
22
- * are returned unchanged.
23
- */
13
+ // SQLite computes `datetime('now')`, the default `prisma-next db init` generates, in UTC but writes it as `YYYY-MM-DD HH:MM:SS`
14
+ // with no zone, and `new Date` reads that form as local time. Values with a zone and date-only values already read as UTC.
24
15
  export const normalizeSqliteDatetime = (value: string): string => value.replace(zonelessDatetime, "$1T$2Z");
25
16
 
26
17
  const decodeAsUtc = (codec: ContractCodec): ContractCodec => ({
27
- id: sqliteDatetimeCodecId,
28
- encode: (value, context) => codec.encode(value, context),
29
18
  decode: (wire: CodecWire, context) => codec.decode(typeof wire === "string" ? normalizeSqliteDatetime(wire) : wire, context),
30
- encodeJson: (value) => codec.encodeJson(value),
31
19
  decodeJson: (json: CodecJson) => codec.decodeJson(typeof json === "string" ? normalizeSqliteDatetime(json) : json),
20
+ encode: (value, context) => codec.encode(value, context),
21
+ encodeJson: (value) => codec.encodeJson(value),
22
+ id: sqliteDatetimeCodecId,
32
23
  });
33
24
 
34
- /**
35
- * Replace the client's codec registry with one that decodes zone-less
36
- * `sqlite/datetime@1` values as UTC.
37
- *
38
- * The registry is the single resolution point for both row decoding and the
39
- * JSON decoding of included relations, so wrapping it covers every read path
40
- * without touching encoding. Prisma Next rejects a second descriptor for an
41
- * already-registered codec id, so an extension pack cannot replace the codec
42
- * itself. Must run before the first query, because the runtime reads the
43
- * registry off the context when it is created.
44
- *
45
- * Codecs are identified by the resolved reference rather than by `codec.id`:
46
- * Prisma Next materializes a codec through an unbound descriptor factory, so
47
- * the instance's `id` accessor throws.
48
- */
25
+ // The registry resolves codecs for rows and included relations alike, and Prisma Next rejects a second descriptor for a registered
26
+ // codec id, so wrapping the registry is the one way to cover every read. It must run before the first query, which reads the
27
+ // registry off the context. Codecs are matched by reference because a materialized codec's `id` accessor throws.
49
28
  export const decodeSqliteDatetimesAsUtc = (context: ExecutionContext<AnySqlContract>): void => {
50
29
  const registry = context.contractCodecs;
51
30
  const descriptors = context.codecDescriptors;
@@ -62,6 +41,10 @@ export const decodeSqliteDatetimesAsUtc = (context: ExecutionContext<AnySqlContr
62
41
  };
63
42
 
64
43
  const utcRegistry: CodecRegistry = {
44
+ forCodecRef: (reference) => {
45
+ const codec = registry.forCodecRef(reference);
46
+ return reference.codecId === sqliteDatetimeCodecId ? wrap(codec) : codec;
47
+ },
65
48
  forColumn: (namespaceId, table, column) => {
66
49
  const codec = registry.forColumn(namespaceId, table, column);
67
50
  if (codec === undefined) {
@@ -70,10 +53,6 @@ export const decodeSqliteDatetimesAsUtc = (context: ExecutionContext<AnySqlContr
70
53
  const reference = descriptors.codecRefForColumn(namespaceId, table, column);
71
54
  return reference?.codecId === sqliteDatetimeCodecId ? wrap(codec) : codec;
72
55
  },
73
- forCodecRef: (reference) => {
74
- const codec = registry.forCodecRef(reference);
75
- return reference.codecId === sqliteDatetimeCodecId ? wrap(codec) : codec;
76
- },
77
56
  };
78
57
 
79
58
  if (!Reflect.set(context, "contractCodecs", utcRegistry)) {
@@ -2,11 +2,7 @@ import { DatabaseSync } from "node:sqlite";
2
2
 
3
3
  export const MEMORY_PATH = ":memory:";
4
4
 
5
- /**
6
- * Prisma Next's SQLite driver opens a fresh `node:sqlite` connection for every
7
- * `RuntimeConnection`, so an in-memory database would give each transaction its
8
- * own empty database.
9
- */
5
+ // Prisma Next's SQLite driver opens a fresh `node:sqlite` connection per `RuntimeConnection`, so each transaction would get its own empty in-memory database.
10
6
  export const assertFileBackedPath = (path: string): void => {
11
7
  if (path === MEMORY_PATH || path.trim().length === 0) {
12
8
  throw new TypeError(
@@ -15,14 +11,8 @@ export const assertFileBackedPath = (path: string): void => {
15
11
  }
16
12
  };
17
13
 
18
- /**
19
- * Apply connect-time pragmas.
20
- *
21
- * The driver already sets `foreign_keys` and `busy_timeout` on every connection
22
- * it opens, but it has no hook for anything else, so durable pragmas such as
23
- * `journal_mode` are applied once against the database file itself.
24
- */
25
- export const applySqlitePragmas = (path: string, pragmas: ReadonlyArray<string>): void => {
14
+ // The driver has no hook for pragmas beyond `foreign_keys` and `busy_timeout`, so durable ones such as `journal_mode` go to the file once.
15
+ export const applySqlitePragmas = (path: string, pragmas: readonly string[]): void => {
26
16
  if (pragmas.length === 0) {
27
17
  return;
28
18
  }
@@ -1,5 +1,5 @@
1
1
  import type { Effect } from "effect";
2
- import type { PrismaError } from "../error.ts";
2
+ import type { PrismaError } from "#error.ts";
3
3
 
4
4
  export const DatabaseTestingTypeId: unique symbol = Symbol.for("@shivaedev/effect-prisma/DatabaseTesting");
5
5
 
@@ -1,7 +1,7 @@
1
1
  import { orm } from "@prisma-next/sql-orm-client";
2
2
  import { Effect, Exit, Semaphore } from "effect";
3
- import type { PrismaError } from "../error.ts";
4
- import { type SettledConnection, type SettledTransaction, settleConnection } from "./adapters/transaction-settlement.ts";
3
+ import type { PrismaError } from "#error.ts";
4
+ import { type SettledConnection, type SettledTransaction, settleConnection } from "#internal/adapters/transaction-settlement.ts";
5
5
  import type { AnySqlContract, DatabaseExecutor } from "./executor.ts";
6
6
  import { fromPrismaPromise } from "./promise.ts";
7
7
 
@@ -14,10 +14,10 @@ export const withTransactionSemaphore = <A, E, R>(
14
14
 
15
15
  export interface TransactionResource<Models extends object, Contract extends AnySqlContract> {
16
16
  readonly connection: SettledConnection;
17
- readonly transaction: SettledTransaction;
18
17
  readonly executor: DatabaseExecutor<Models, Contract> & {
19
18
  readonly querySemaphore: Semaphore.Semaphore;
20
19
  };
20
+ readonly transaction: SettledTransaction;
21
21
  }
22
22
 
23
23
  export const acquireTransaction = <Models extends object, Contract extends AnySqlContract>(
@@ -31,13 +31,12 @@ export const acquireTransaction = <Models extends object, Contract extends AnySq
31
31
  try {
32
32
  const transaction = await connection.transaction();
33
33
  const transactionOrm = orm({
34
- runtime: transaction,
35
34
  context: current.client.context,
35
+ runtime: transaction,
36
36
  });
37
37
 
38
38
  return {
39
39
  connection,
40
- transaction,
41
40
  executor: {
42
41
  client: current.client,
43
42
  identity: current.identity,
@@ -51,6 +50,7 @@ export const acquireTransaction = <Models extends object, Contract extends AnySq
51
50
  transactionIdentity: {},
52
51
  transactionSemaphore: current.transactionSemaphore,
53
52
  },
53
+ transaction,
54
54
  };
55
55
  } catch (error) {
56
56
  await connection.destroy(error).catch(() => undefined);
@@ -62,7 +62,7 @@ type AnyNullableField<Fields, Names extends readonly string[]> = Names extends r
62
62
 
63
63
  export type IncludedRelationValue<Contract extends AnyPostgresContract, Model extends string, RelationName extends string, Value> =
64
64
  RelationCardinalityOf<Contract, Model, RelationName> extends "1:N" | "N:M"
65
- ? Array<Value>
65
+ ? Value[]
66
66
  : RelationCardinalityOf<Contract, Model, RelationName> extends "N:1"
67
67
  ? AnyNullableField<ModelFields<Contract, Model>, RelationLocalFields<Contract, Model, RelationName>> extends true
68
68
  ? Value | null
@@ -1,5 +1,5 @@
1
1
  import type { DefaultModelRow, Collection as PrismaCollection, RelationNames } from "@prisma-next/sql-orm-client";
2
- import type { Relation, RelationQuery } from "../relation.ts";
2
+ import type { Relation, RelationQuery } from "#relation.ts";
3
3
  import type { AnyPostgresContract, IncludedRelationValue, IsToManyRelation, RelatedModelNameOf } from "./include-metadata.ts";
4
4
 
5
5
  type Simplify<Value> = { [Key in keyof Value]: Value[Key] };
@@ -43,40 +43,42 @@ type WithIncludedRelation<Collection, Contract extends AnyPostgresContract, Mode
43
43
  ? PrismaCollection<Contract, Model, Simplify<Row & { readonly [Key in RelationName]: Value }>, State>
44
44
  : never;
45
45
 
46
+ interface IncludeOverloads<Collection, Contract extends AnyPostgresContract, Model extends string, DatabaseId> {
47
+ <RelationName extends RelationNames<Contract, Model>>(
48
+ relationName: RelationName,
49
+ ): Relation<
50
+ WithIncludedRelation<
51
+ Collection,
52
+ Contract,
53
+ Model,
54
+ RelationName,
55
+ IncludedRelationValue<Contract, Model, RelationName, DefaultModelRow<Contract, RelatedModelNameOf<Contract, Model, RelationName>>>
56
+ >,
57
+ Contract,
58
+ Model,
59
+ DatabaseId
60
+ >;
61
+ <RelationName extends RelationNames<Contract, Model>, Query>(
62
+ relationName: RelationName,
63
+ query: Query & AcceptRelatedQuery<Query, Contract, RelatedModelNameOf<Contract, Model, RelationName>, DatabaseId>,
64
+ ): Relation<
65
+ WithIncludedRelation<Collection, Contract, Model, RelationName, IncludedQueryValue<Query, Contract, Model, RelationName>>,
66
+ Contract,
67
+ Model,
68
+ DatabaseId
69
+ >;
70
+ <RelationName extends RelationNames<Contract, Model>, Shape extends Readonly<Record<string, unknown>>>(
71
+ relationName: RelationName,
72
+ shape: IsToManyRelation<Contract, Model, RelationName> extends true
73
+ ? keyof Shape extends never
74
+ ? never
75
+ : Shape & AcceptRelatedQueryShape<Shape, Contract, RelatedModelNameOf<Contract, Model, RelationName>, DatabaseId>
76
+ : never,
77
+ ): Relation<WithIncludedRelation<Collection, Contract, Model, RelationName, QueryShapeValue<Shape>>, Contract, Model, DatabaseId>;
78
+ }
79
+
46
80
  export type IncludeMethod<Collection, Contract, Model extends string, DatabaseId> = Contract extends AnyPostgresContract
47
81
  ? Collection extends PrismaCollection<Contract, Model, infer _Row, infer _State>
48
- ? {
49
- include<RelationName extends RelationNames<Contract, Model>>(
50
- relationName: RelationName,
51
- ): Relation<
52
- WithIncludedRelation<
53
- Collection,
54
- Contract,
55
- Model,
56
- RelationName,
57
- IncludedRelationValue<Contract, Model, RelationName, DefaultModelRow<Contract, RelatedModelNameOf<Contract, Model, RelationName>>>
58
- >,
59
- Contract,
60
- Model,
61
- DatabaseId
62
- >;
63
- include<RelationName extends RelationNames<Contract, Model>, Query>(
64
- relationName: RelationName,
65
- query: Query & AcceptRelatedQuery<Query, Contract, RelatedModelNameOf<Contract, Model, RelationName>, DatabaseId>,
66
- ): Relation<
67
- WithIncludedRelation<Collection, Contract, Model, RelationName, IncludedQueryValue<Query, Contract, Model, RelationName>>,
68
- Contract,
69
- Model,
70
- DatabaseId
71
- >;
72
- include<RelationName extends RelationNames<Contract, Model>, Shape extends Readonly<Record<string, unknown>>>(
73
- relationName: RelationName,
74
- shape: IsToManyRelation<Contract, Model, RelationName> extends true
75
- ? keyof Shape extends never
76
- ? never
77
- : Shape & AcceptRelatedQueryShape<Shape, Contract, RelatedModelNameOf<Contract, Model, RelationName>, DatabaseId>
78
- : never,
79
- ): Relation<WithIncludedRelation<Collection, Contract, Model, RelationName, QueryShapeValue<Shape>>, Contract, Model, DatabaseId>;
80
- }
82
+ ? { include: IncludeOverloads<Collection, Contract, Model, DatabaseId> }
81
83
  : Record<never, never>
82
84
  : Record<never, never>;
@@ -9,11 +9,11 @@ import type {
9
9
  Collection as PrismaCollection,
10
10
  } from "@prisma-next/sql-orm-client";
11
11
  import type { Effect, Stream } from "effect";
12
- import type { PrismaError } from "../error.ts";
13
- import type { CollectionResult, Relation, RelationQuery } from "../relation.ts";
12
+ import type { PrismaError } from "#error.ts";
13
+ import type { CollectionResult, Relation, RelationQuery } from "#relation.ts";
14
14
  import type { IncludeMethod } from "./include.ts";
15
15
 
16
- type AnyFunction = (...arguments_: ReadonlyArray<never>) => unknown;
16
+ type AnyFunction = (...arguments_: readonly never[]) => unknown;
17
17
  type AnyPostgresContract = PrismaContract<SqlStorage>;
18
18
  type FieldTuple<Row> = readonly [keyof Row & string, ...(keyof Row & string)[]];
19
19
  type Simplify<Value> = { [Key in keyof Value]: Value[Key] };
@@ -25,9 +25,9 @@ type TerminalMethod<Method> = Method extends (...arguments_: infer Arguments) =>
25
25
  type SelectMethod<Collection, Contract, Model extends string, DatabaseId> = Contract extends AnyPostgresContract
26
26
  ? Collection extends PrismaCollection<Contract, Model, infer _Row, infer State>
27
27
  ? {
28
- select<Fields extends FieldTuple<DefaultModelRow<Contract, Model>>>(
28
+ select: <Fields extends FieldTuple<DefaultModelRow<Contract, Model>>>(
29
29
  ...fields: Fields
30
- ): Relation<PrismaCollection<Contract, Model, Pick<DefaultModelRow<Contract, Model>, Fields[number]>, State>, Contract, Model, DatabaseId>;
30
+ ) => Relation<PrismaCollection<Contract, Model, Pick<DefaultModelRow<Contract, Model>, Fields[number]>, State>, Contract, Model, DatabaseId>;
31
31
  }
32
32
  : Record<never, never>
33
33
  : Collection extends { select: infer Select extends AnyFunction }
@@ -46,16 +46,16 @@ type AggregateConfigure<Collection> = Collection extends {
46
46
 
47
47
  type AggregateSuccess<Collection, Contract extends AnyPostgresContract, Model extends string, Spec extends AggregateSpec> =
48
48
  Collection extends GroupedCollection<Contract, Model, infer Fields extends FieldTuple<DefaultModelRow<Contract, Model>>>
49
- ? Array<Simplify<Pick<DefaultModelRow<Contract, Model>, Fields[number]> & AggregateResult<Spec>>>
49
+ ? Simplify<Pick<DefaultModelRow<Contract, Model>, Fields[number]> & AggregateResult<Spec>>[]
50
50
  : AggregateResult<Spec>;
51
51
 
52
52
  type AggregateMethod<Collection, Contract, Model extends string> = Contract extends AnyPostgresContract
53
53
  ? Collection extends { aggregate: AnyFunction }
54
54
  ? {
55
- aggregate<Spec extends AggregateSpec>(
55
+ aggregate: <Spec extends AggregateSpec>(
56
56
  make: (aggregate: AggregateBuilder<Contract, Model>) => Spec,
57
57
  configure?: AggregateConfigure<Collection>,
58
- ): Effect.Effect<AggregateSuccess<Collection, Contract, Model, Spec>, PrismaError>;
58
+ ) => Effect.Effect<AggregateSuccess<Collection, Contract, Model, Spec>, PrismaError>;
59
59
  }
60
60
  : Record<never, never>
61
61
  : Record<never, never>;
@@ -63,16 +63,18 @@ type AggregateMethod<Collection, Contract, Model extends string> = Contract exte
63
63
  type CollectionMethods<Collection, Contract, Model extends string, DatabaseId> = Contract extends AnyPostgresContract
64
64
  ? Collection extends PrismaCollection<Contract, Model, infer _Row, infer State>
65
65
  ? {
66
- groupBy<Fields extends FieldTuple<DefaultModelRow<Contract, Model>>>(
66
+ groupBy: <Fields extends FieldTuple<DefaultModelRow<Contract, Model>>>(
67
67
  ...fields: Fields
68
- ): Relation<GroupedCollection<Contract, Model, Fields>, Contract, Model, DatabaseId>;
69
- cursor(
68
+ ) => Relation<GroupedCollection<Contract, Model, Fields>, Contract, Model, DatabaseId>;
69
+ cursor: (
70
70
  values: State extends { readonly hasOrderBy: true } ? Partial<Record<keyof DefaultModelRow<Contract, Model> & string, unknown>> : never,
71
- ): Relation<Collection, Contract, Model, DatabaseId>;
72
- distinct<Fields extends FieldTuple<DefaultModelRow<Contract, Model>>>(...fields: Fields): Relation<Collection, Contract, Model, DatabaseId>;
73
- distinctOn<Fields extends FieldTuple<DefaultModelRow<Contract, Model>>>(
71
+ ) => Relation<Collection, Contract, Model, DatabaseId>;
72
+ distinct: <Fields extends FieldTuple<DefaultModelRow<Contract, Model>>>(
73
+ ...fields: Fields
74
+ ) => Relation<Collection, Contract, Model, DatabaseId>;
75
+ distinctOn: <Fields extends FieldTuple<DefaultModelRow<Contract, Model>>>(
74
76
  ...fields: State extends { readonly hasOrderBy: true } ? Fields : never
75
- ): Relation<Collection, Contract, Model, DatabaseId>;
77
+ ) => Relation<Collection, Contract, Model, DatabaseId>;
76
78
  } & (State extends { readonly hasWhere: true }
77
79
  ? {
78
80
  readonly [Key in "delete" | "deleteAll" | "deleteCount"]: TerminalMethod<Collection[Key]>;
@@ -85,15 +87,15 @@ type CollectionConveniences<Collection, Contract, Model extends string, Database
85
87
  ? Collection extends PrismaCollection<Contract, Model, infer Row, infer _State>
86
88
  ? {
87
89
  readonly stream: Stream.Stream<Row, PrismaError>;
88
- count(): RelationQuery<number, Contract, Model, DatabaseId>;
89
- exists(): Effect.Effect<boolean, PrismaError>;
90
+ count: () => RelationQuery<number, Contract, Model, DatabaseId>;
91
+ exists: () => Effect.Effect<boolean, PrismaError>;
90
92
  }
91
93
  : Record<never, never>
92
94
  : CollectionResult<Collection> extends ReadonlyArray<infer Row>
93
95
  ? {
94
96
  readonly stream: Stream.Stream<Row, PrismaError>;
95
- count(): Effect.Effect<number, PrismaError>;
96
- exists(): Effect.Effect<boolean, PrismaError>;
97
+ count: () => Effect.Effect<number, PrismaError>;
98
+ exists: () => Effect.Effect<boolean, PrismaError>;
97
99
  }
98
100
  : Record<never, never>;
99
101
 
package/src/relation.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type { Effect } from "effect";
2
+ import type { PrismaRelationMethods } from "#relation/prisma-methods.ts";
2
3
  import type { PrismaError } from "./error.ts";
3
- import type { PrismaRelationMethods } from "./relation/prisma-methods.ts";
4
4
 
5
- type AnyFunction = (...arguments_: ReadonlyArray<never>) => unknown;
5
+ type AnyFunction = (...arguments_: readonly never[]) => unknown;
6
6
 
7
7
  export type CollectionResult<Collection> = Collection extends {
8
- all(): infer Result;
8
+ all: () => infer Result;
9
9
  }
10
10
  ? Awaited<Result>
11
11
  : never;