@aventara/prisma7-adapter 1.0.0-rc.12 → 1.0.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +59 -1
- package/dist/adapter-warning.emitter.d.ts +2 -0
- package/dist/adapter-warning.emitter.js +8 -0
- package/dist/discovery/client-shape.verifier.d.ts +8 -0
- package/dist/discovery/client-shape.verifier.js +14 -0
- package/dist/execution/shared-connection.router.d.ts +25 -0
- package/dist/execution/shared-connection.router.js +70 -0
- package/dist/execution/transaction.executor.d.ts +2 -0
- package/dist/execution/transaction.executor.js +9 -3
- package/dist/prisma.adapter.d.ts +2 -1
- package/dist/prisma.adapter.js +38 -25
- package/dist/provider/provider-profile.d.ts +4 -0
- package/dist/provider/provider-profile.js +8 -0
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @aventara/prisma7-adapter
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 66b31b5: With SQLite through `@prisma/adapter-better-sqlite3`, a plain operation no longer runs inside another request's open
|
|
8
|
+
transaction. That driver has one connection, and an interactive transaction holds it while it waits between
|
|
9
|
+
statements; Prisma ran a plain statement on that same connection without waiting, so an unforced write made meanwhile
|
|
10
|
+
rolled back with the other request's transaction, and an unforced read saw its uncommitted rows. With that driver, an
|
|
11
|
+
operation that is not part of a transaction, and a `ctx.$sql` that is not, now each run in a transaction of their own
|
|
12
|
+
that holds only that call, so they wait until the open transaction ends. `transactionOptions` apply to that wait: past
|
|
13
|
+
`maxWait` the operation answers `A3002`, and `timeout` limits a single plain operation too. A plain call made from
|
|
14
|
+
inside a transaction's own async context (a stage calling the root Framework instead of its `ctx`) joins that
|
|
15
|
+
transaction instead of waiting on it, and warns (`AVENTARA_PRISMA_SQLITE_TRANSACTION_JOINED`) once per Resource and operation and once for `ctx.$sql`,
|
|
16
|
+
because a helper shared across requests can carry that context into another request's call. PostgreSQL and D1 run as
|
|
17
|
+
before. A mismatched `driver` declaration is now refused at construction: `createPrismaAdapter` throws when the Prisma
|
|
18
|
+
client was constructed with another driver adapter than the declared `driver`, and names the supported set
|
|
19
|
+
(`@prisma/adapter-better-sqlite3`, `@prisma/adapter-pg`, `@prisma/adapter-d1`); a client built with any other driver
|
|
20
|
+
adapter is not supported.
|
|
21
|
+
- Updated dependencies [6c3e25c]
|
|
22
|
+
- Updated dependencies [2aaa27d]
|
|
23
|
+
- Updated dependencies [f3b291f]
|
|
24
|
+
- Updated dependencies [2aaa27d]
|
|
25
|
+
- Updated dependencies [e4e8abd]
|
|
26
|
+
- Updated dependencies [e662048]
|
|
27
|
+
- Updated dependencies [f3b291f]
|
|
28
|
+
- Updated dependencies [f3b291f]
|
|
29
|
+
- Updated dependencies [6c3e25c]
|
|
30
|
+
- Updated dependencies [6c3e25c]
|
|
31
|
+
- Updated dependencies [a708188]
|
|
32
|
+
- Updated dependencies [a9260d2]
|
|
33
|
+
- @aventara/core@1.0.0-rc.13
|
|
34
|
+
|
|
3
35
|
## 1.0.0-rc.12
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -50,7 +50,10 @@ const framework = await createFramework({ api: "/api", adapter });
|
|
|
50
50
|
|
|
51
51
|
The caller declares `provider` and `driver`. The adapter does not inspect client internals to guess
|
|
52
52
|
them: the driver decides whether transactions are advertised at all, and a wrong guess would be worse
|
|
53
|
-
than a declaration that is checked.
|
|
53
|
+
than a declaration that is checked. It does check the declaration: when the client names the driver
|
|
54
|
+
adapter it was constructed with (Prisma 7.10 keeps it where its own D1 check reads it), a `driver` that
|
|
55
|
+
differs refuses to start (`AvPrismaDiscoveryArtifactError`, naming both). A client that does not say,
|
|
56
|
+
such as a test double, is not compared.
|
|
54
57
|
|
|
55
58
|
`createPrismaAdapter` is `async`. Construction verifies the discovery artifact, and the digest it
|
|
56
59
|
checks is a `crypto.subtle` promise, so every check runs once, here, before an adapter exists.
|
|
@@ -90,6 +93,61 @@ The supported isolation levels come from the declared provider. PostgreSQL admit
|
|
|
90
93
|
`ReadCommitted`, `RepeatableRead` and `Serializable`; SQLite admits `Serializable` only. Asking for a
|
|
91
94
|
level the provider does not support fails the transaction; see [`P2028`](#p2028-covers-seven-situations-and-three-of-them-are-separable).
|
|
92
95
|
|
|
96
|
+
### SQLite: one connection
|
|
97
|
+
|
|
98
|
+
`@prisma/adapter-better-sqlite3` holds one connection, and an interactive transaction holds it from `BEGIN`
|
|
99
|
+
to `COMMIT`, including while it waits between statements. Prisma runs a plain statement on that same
|
|
100
|
+
connection without waiting, so a plain call made meanwhile would run inside the open transaction: it would
|
|
101
|
+
see its uncommitted rows and roll back with it. With that driver, an operation that is not part of a
|
|
102
|
+
transaction, and a `ctx.$sql` that is not, each run in a `$transaction` of their own that holds only that
|
|
103
|
+
call. They wait on the driver's own lock, so operations and transactions queue for the connection and run
|
|
104
|
+
one at a time. `transactionOptions` apply to the wait: past `maxWait` the operation answers `A3002`, and
|
|
105
|
+
`timeout` limits a single plain operation too. `@prisma/adapter-pg` checks a connection of its own out of
|
|
106
|
+
the pool for each transaction, and D1 runs none, so both stay as they were. Calls made on the Prisma client
|
|
107
|
+
directly, outside Aventara, are not covered.
|
|
108
|
+
|
|
109
|
+
#### A call from inside a transaction joins it, and warns
|
|
110
|
+
|
|
111
|
+
A plain call made from inside an open transaction's own async context, such as a stage that calls the root
|
|
112
|
+
Framework instead of its `ctx`, would otherwise queue behind the very transaction it runs in and fail after
|
|
113
|
+
`maxWait`. So it joins that transaction instead: it runs on the transaction's connection and commits or
|
|
114
|
+
rolls back with it, as it did before the queue existed. The adapter knows which transaction a call belongs
|
|
115
|
+
to only from Node's `AsyncLocalStorage`. The first time a given Resource and operation joins, the process
|
|
116
|
+
warns once (and once for `ctx.$sql`):
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
(node:4711) [AVENTARA_PRISMA_SQLITE_TRANSACTION_JOINED] AventaraWarning: Author create.many ran inside an
|
|
120
|
+
open SQLite transaction (better-sqlite3) instead of waiting for it: it was called from that transaction's
|
|
121
|
+
async context, so it joined the transaction and commits or rolls back with it. …
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
A join you meant (the stage above) is fine, and the warning is expected for it. A join you did not mean
|
|
125
|
+
comes from a helper shared across requests that runs another request's work in the async context it was
|
|
126
|
+
first called from. A naive batcher is the usual one:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
let pending: Array<{ email: string }> = [];
|
|
130
|
+
let flushing: ReturnType<typeof setTimeout> | undefined;
|
|
131
|
+
|
|
132
|
+
export function addAuthor(email: string) {
|
|
133
|
+
pending.push({ email });
|
|
134
|
+
// Scheduled inside request A's transaction (say, from an after hook), the timer
|
|
135
|
+
// keeps A's async context. Request B's author, added before the flush, is then
|
|
136
|
+
// written inside A's transaction and rolls back if A does.
|
|
137
|
+
flushing ??= setTimeout(() => {
|
|
138
|
+
const data = pending;
|
|
139
|
+
pending = [];
|
|
140
|
+
flushing = undefined;
|
|
141
|
+
void framework.application.Author.create.many({ data });
|
|
142
|
+
}, 50);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Give such a helper its own context: bind its callback with `AsyncResource.bind` from `node:async_hooks`
|
|
147
|
+
when it is created (`const flush = AsyncResource.bind(() => { … })`, created at module load, outside any
|
|
148
|
+
transaction), or schedule its timers from outside a transaction. The join is never refused, because code
|
|
149
|
+
that joins on purpose has to keep working.
|
|
150
|
+
|
|
93
151
|
---
|
|
94
152
|
|
|
95
153
|
## The generate step, and the artifact it emits
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function emitAdapterWarning(message, code) {
|
|
2
|
+
const emitWarning = globalThis.process?.emitWarning;
|
|
3
|
+
if (typeof emitWarning === "function") {
|
|
4
|
+
emitWarning(message, { type: "AventaraWarning", code });
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
globalThis.console?.warn(`AventaraWarning: ${message}`);
|
|
8
|
+
}
|
|
@@ -6,3 +6,11 @@ import type { PrismaClientShape } from "./discovery-artifact.interface.js";
|
|
|
6
6
|
* @throws AvPrismaDiscoveryArtifactError
|
|
7
7
|
*/
|
|
8
8
|
export declare function assertClientMatchesArtifact(recorded: PrismaClientShape, client: unknown): void;
|
|
9
|
+
/**
|
|
10
|
+
* Throws when the Prisma client in hand was constructed with another driver
|
|
11
|
+
* adapter than the declared `driver` (one outside the supported set included);
|
|
12
|
+
* skips when the client does not say.
|
|
13
|
+
*
|
|
14
|
+
* @throws AvPrismaDiscoveryArtifactError
|
|
15
|
+
*/
|
|
16
|
+
export declare function assertClientUsesDeclaredDriver(driver: string, client: unknown): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { delegateKeyOf } from "../execution/operation.executor.js";
|
|
2
|
+
import { PRISMA_DRIVERS } from "../provider/provider-profile.js";
|
|
2
3
|
import { AvPrismaDiscoveryArtifactError } from "./artifact.loader.js";
|
|
3
4
|
const SHOWN = 3;
|
|
4
5
|
function readMember(value, key) {
|
|
@@ -91,3 +92,16 @@ export function assertClientMatchesArtifact(recorded, client) {
|
|
|
91
92
|
const more = drift.length > SHOWN ? `; and ${drift.length - SHOWN} more` : "";
|
|
92
93
|
throw new AvPrismaDiscoveryArtifactError(`The Prisma client has changed since the discovery artifact was generated (${shown}${more}):`, "run `npm run aventara:prepare` (the adapter's `aventara-prisma7-generate`) to regenerate it, then restart.");
|
|
93
94
|
}
|
|
95
|
+
const SUPPORTED_DRIVERS = `${PRISMA_DRIVERS.slice(0, -1)
|
|
96
|
+
.map((name) => `\`${name}\``)
|
|
97
|
+
.join(", ")} and \`${PRISMA_DRIVERS[PRISMA_DRIVERS.length - 1]}\``;
|
|
98
|
+
export function assertClientUsesDeclaredDriver(driver, client) {
|
|
99
|
+
const constructedWith = readMember(readMember(readMember(client, "_engineConfig"), "adapter"), "adapterName");
|
|
100
|
+
if (typeof constructedWith !== "string" || constructedWith === driver) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const supported = PRISMA_DRIVERS.includes(constructedWith);
|
|
104
|
+
throw new AvPrismaDiscoveryArtifactError(`The Prisma client is constructed with the driver adapter \`${constructedWith}\`, but createPrismaAdapter was given \`driver: "${driver}"\`.`, `@aventara/prisma7-adapter supports the driver adapters ${SUPPORTED_DRIVERS}; ${supported
|
|
105
|
+
? `declare \`driver: "${constructedWith}"\`, then restart.`
|
|
106
|
+
: `a client constructed with any other driver adapter is not supported.`}`);
|
|
107
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AvAdapterOperation } from "@aventara/core/internal";
|
|
2
|
+
import type { AvPrismaDelegateHost, AvPrismaTransactionOptions } from "../prisma-client.interface.js";
|
|
3
|
+
/** What a plain call is, for the warning a join emits: an operation, or a `ctx.$sql` statement. */
|
|
4
|
+
export type PrismaPlainCall = AvAdapterOperation | "ctx.$sql";
|
|
5
|
+
/** How plain work and transactions share a driver's one connection. */
|
|
6
|
+
export type PrismaSharedConnectionRouter = {
|
|
7
|
+
/**
|
|
8
|
+
* Runs plain work on the transaction this async flow is inside (and warns,
|
|
9
|
+
* once per `call`), or else in a one-statement transaction of its own; a
|
|
10
|
+
* failure to open or commit that transaction is classified through the
|
|
11
|
+
* closed table.
|
|
12
|
+
*/
|
|
13
|
+
plain<T>(call: PrismaPlainCall, work: (client: AvPrismaDelegateHost) => Promise<T>): Promise<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Runs a transaction's work so that plain work issued from inside it joins
|
|
16
|
+
* it. The work's own promise is returned as is: its rejection reaches Prisma
|
|
17
|
+
* by reference (core recognises its abort sentinel by identity).
|
|
18
|
+
*/
|
|
19
|
+
within<T>(transactional: AvPrismaDelegateHost, work: () => Promise<T>): Promise<T>;
|
|
20
|
+
};
|
|
21
|
+
/** The router for a shared-connection driver's client. */
|
|
22
|
+
export declare function createSharedConnectionRouter(dependencies: {
|
|
23
|
+
readonly client: AvPrismaDelegateHost;
|
|
24
|
+
readonly transactionOptions?: AvPrismaTransactionOptions;
|
|
25
|
+
}): PrismaSharedConnectionRouter;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { emitAdapterWarning } from "../adapter-warning.emitter.js";
|
|
2
|
+
import { classifyPrismaStatementError, prismaOutcomeFailure, } from "../errors/error.normalizer.js";
|
|
3
|
+
const TRANSACTION_JOINED_WARNING = "AVENTARA_PRISMA_SQLITE_TRANSACTION_JOINED";
|
|
4
|
+
const warnedJoins = new Set();
|
|
5
|
+
function warnJoined(call) {
|
|
6
|
+
const subject = call === "ctx.$sql"
|
|
7
|
+
? call
|
|
8
|
+
: `${call.resource} ${call.family}.${call.variant}`;
|
|
9
|
+
if (warnedJoins.has(subject)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
warnedJoins.add(subject);
|
|
13
|
+
emitAdapterWarning(`${subject} ran inside an open SQLite transaction (better-sqlite3) instead of waiting for it: it was called from that transaction's async context, so it joined the transaction and commits or rolls back with it. That is intended when the transaction's own work makes the call, such as a stage calling the root Framework instead of its ctx. If the call came through a helper shared across requests (a batcher, a queue, a timer first scheduled inside a transaction), it is another request's call and has joined a transaction that is not its own: bind the helper's callbacks with AsyncResource.bind, or schedule them outside the transaction. ${call === "ctx.$sql" ? "Warned once for ctx.$sql." : "Warned once per Resource and operation."}`, TRANSACTION_JOINED_WARNING);
|
|
14
|
+
}
|
|
15
|
+
function createFlowStorage() {
|
|
16
|
+
const process = globalThis.process;
|
|
17
|
+
const hooks = process?.getBuiltinModule?.("node:async_hooks");
|
|
18
|
+
return typeof hooks?.AsyncLocalStorage === "function"
|
|
19
|
+
? new hooks.AsyncLocalStorage()
|
|
20
|
+
: undefined;
|
|
21
|
+
}
|
|
22
|
+
export function createSharedConnectionRouter(dependencies) {
|
|
23
|
+
const flows = createFlowStorage();
|
|
24
|
+
const within = (transactional, work) => {
|
|
25
|
+
if (flows === undefined) {
|
|
26
|
+
return work();
|
|
27
|
+
}
|
|
28
|
+
const entry = { client: transactional, open: true };
|
|
29
|
+
const settled = flows.run(entry, work);
|
|
30
|
+
const close = () => {
|
|
31
|
+
entry.open = false;
|
|
32
|
+
};
|
|
33
|
+
settled.then(close, close);
|
|
34
|
+
return settled;
|
|
35
|
+
};
|
|
36
|
+
const plain = async (call, work) => {
|
|
37
|
+
const joined = flows?.getStore();
|
|
38
|
+
if (joined?.open === true) {
|
|
39
|
+
warnJoined(call);
|
|
40
|
+
return work(joined.client);
|
|
41
|
+
}
|
|
42
|
+
let thrownByWork = false;
|
|
43
|
+
const run = async (transactional) => {
|
|
44
|
+
try {
|
|
45
|
+
return await within(transactional, () => work(transactional));
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
thrownByWork = true;
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const options = dependencies.transactionOptions;
|
|
53
|
+
try {
|
|
54
|
+
return await (options === undefined
|
|
55
|
+
? dependencies.client.$transaction(run)
|
|
56
|
+
: dependencies.client.$transaction(run, options));
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (thrownByWork) {
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
const classification = classifyPrismaStatementError(error);
|
|
63
|
+
if (classification.kind === "outcome") {
|
|
64
|
+
throw prismaOutcomeFailure(classification.code);
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
return { plain, within };
|
|
70
|
+
}
|
|
@@ -4,6 +4,7 @@ import type { PrismaResourceTranslation } from "../discovery/discovery-artifact.
|
|
|
4
4
|
import type { PrismaBackReferenceTable } from "../errors/back-reference.locator.js";
|
|
5
5
|
import type { AvPrismaDelegateHost, AvPrismaTransactionOptions } from "../prisma-client.interface.js";
|
|
6
6
|
import type { PrismaBigintBinding } from "../provider/provider-profile.js";
|
|
7
|
+
import type { PrismaSharedConnectionRouter } from "./shared-connection.router.js";
|
|
7
8
|
type PrismaTransactionExecutorDependencies = {
|
|
8
9
|
readonly client: AvPrismaDelegateHost;
|
|
9
10
|
readonly model: AvAdapterModel;
|
|
@@ -12,6 +13,7 @@ type PrismaTransactionExecutorDependencies = {
|
|
|
12
13
|
readonly backReferences: PrismaBackReferenceTable;
|
|
13
14
|
readonly bigintBinding: PrismaBigintBinding;
|
|
14
15
|
readonly transactionOptions?: AvPrismaTransactionOptions;
|
|
16
|
+
readonly sharedConnection?: PrismaSharedConnectionRouter;
|
|
15
17
|
};
|
|
16
18
|
type PrismaTransactionRunner<M extends AvAdapterModel> = <T>(work: (executor: AvAdapterTransactionExecutor<M>) => Promise<T>) => Promise<T>;
|
|
17
19
|
export declare function createPrismaTransactionExecutor<M extends AvAdapterModel>(dependencies: PrismaTransactionExecutorDependencies): PrismaTransactionRunner<M>;
|
|
@@ -13,7 +13,13 @@ export function createPrismaTransactionExecutor(dependencies) {
|
|
|
13
13
|
}),
|
|
14
14
|
executeSql: createPrismaSqlStatementExecutor(transactional),
|
|
15
15
|
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const router = dependencies.sharedConnection;
|
|
17
|
+
return (work) => {
|
|
18
|
+
const run = async (transactional) => router === undefined
|
|
19
|
+
? work(scopedExecutorFor(transactional))
|
|
20
|
+
: router.within(transactional, () => work(scopedExecutorFor(transactional)));
|
|
21
|
+
return options === undefined
|
|
22
|
+
? dependencies.client.$transaction(run)
|
|
23
|
+
: dependencies.client.$transaction(run, options);
|
|
24
|
+
};
|
|
19
25
|
}
|
package/dist/prisma.adapter.d.ts
CHANGED
|
@@ -41,7 +41,8 @@ type PrismaAdapterConstruction<TClient extends AvPrismaDelegateHost, M extends A
|
|
|
41
41
|
* ```
|
|
42
42
|
*
|
|
43
43
|
* @throws `AvPrismaDiscoveryArtifactError` when the artifact is stale, corrupt or
|
|
44
|
-
* was generated for another Prisma major
|
|
44
|
+
* was generated for another Prisma major (its message says how to regenerate
|
|
45
|
+
* it), or when the client was constructed with another driver than `driver`.
|
|
45
46
|
*/
|
|
46
47
|
export declare function createPrismaAdapter<TClient extends AvPrismaDelegateHost, M extends AvPrismaAdapterModel = AvPrismaAdapterModel>(options: PrismaAdapterConstruction<TClient, M>): Promise<AvAdapter<M>>;
|
|
47
48
|
export {};
|
package/dist/prisma.adapter.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { emitAdapterWarning } from "./adapter-warning.emitter.js";
|
|
1
2
|
import { loadDiscoveryArtifact } from "./discovery/artifact.loader.js";
|
|
2
|
-
import { assertClientMatchesArtifact } from "./discovery/client-shape.verifier.js";
|
|
3
|
+
import { assertClientMatchesArtifact, assertClientUsesDeclaredDriver, } from "./discovery/client-shape.verifier.js";
|
|
3
4
|
import { locateInstalledPrismaClientVersion } from "./discovery/installed-client.locator.js";
|
|
4
5
|
import { derivePrismaBackReferences } from "./errors/back-reference.locator.js";
|
|
5
6
|
import { createPrismaOperationExecutor } from "./execution/operation.executor.js";
|
|
7
|
+
import { createSharedConnectionRouter } from "./execution/shared-connection.router.js";
|
|
6
8
|
import { createPrismaSqlStatementExecutor } from "./execution/sql-statement.executor.js";
|
|
7
9
|
import { createPrismaTransactionExecutor } from "./execution/transaction.executor.js";
|
|
8
10
|
import { describePrismaAdapter } from "./prisma-adapter.description.js";
|
|
9
|
-
import { resolveBigintBinding, } from "./provider/provider-profile.js";
|
|
11
|
+
import { resolveBigintBinding, resolveTransactionConnection, } from "./provider/provider-profile.js";
|
|
10
12
|
const PRISMA_VERSION_UNCHECKED_WARNING = "AVENTARA_PRISMA_VERSION_UNCHECKED";
|
|
11
13
|
function installedClientVersion(discovery, override) {
|
|
12
14
|
if (override !== undefined) {
|
|
@@ -17,16 +19,7 @@ function installedClientVersion(discovery, override) {
|
|
|
17
19
|
return located.version;
|
|
18
20
|
}
|
|
19
21
|
const message = `the installed @prisma/client's version could not be read (${located.reason}), so the discovery artifact's Prisma major (${String(discovery.prismaVersion).split(".")[0]}) was not compared with the installed @prisma/client's. Deploy @prisma/client where the application resolves it, or pass installedPrismaVersion to createPrismaAdapter.`;
|
|
20
|
-
|
|
21
|
-
if (typeof emitWarning === "function") {
|
|
22
|
-
emitWarning(message, {
|
|
23
|
-
type: "AventaraWarning",
|
|
24
|
-
code: PRISMA_VERSION_UNCHECKED_WARNING,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
globalThis.console?.warn(`AventaraWarning: ${message}`);
|
|
29
|
-
}
|
|
22
|
+
emitAdapterWarning(message, PRISMA_VERSION_UNCHECKED_WARNING);
|
|
30
23
|
return undefined;
|
|
31
24
|
}
|
|
32
25
|
export async function createPrismaAdapter(options) {
|
|
@@ -36,6 +29,7 @@ export async function createPrismaAdapter(options) {
|
|
|
36
29
|
installedPrismaVersion: installedClientVersion(options.discovery, options.installedPrismaVersion),
|
|
37
30
|
});
|
|
38
31
|
assertClientMatchesArtifact(options.discovery.client, options.client);
|
|
32
|
+
assertClientUsesDeclaredDriver(options.driver, options.client);
|
|
39
33
|
const model = description.model;
|
|
40
34
|
const referentialActions = description.referentialActions;
|
|
41
35
|
const backReferences = derivePrismaBackReferences(options.discovery.model);
|
|
@@ -43,18 +37,38 @@ export async function createPrismaAdapter(options) {
|
|
|
43
37
|
const computedFields = description.computedFields === undefined
|
|
44
38
|
? {}
|
|
45
39
|
: { computedFields: description.computedFields };
|
|
46
|
-
const
|
|
47
|
-
|
|
40
|
+
const translation = options.discovery.translation;
|
|
41
|
+
const transactionOptions = options.transactionOptions === undefined
|
|
42
|
+
? {}
|
|
43
|
+
: { transactionOptions: options.transactionOptions };
|
|
44
|
+
const sharedConnection = resolveTransactionConnection(options.driver) === "shared"
|
|
45
|
+
? createSharedConnectionRouter({
|
|
46
|
+
client: options.client,
|
|
47
|
+
...transactionOptions,
|
|
48
|
+
})
|
|
49
|
+
: undefined;
|
|
50
|
+
const executorDependencies = {
|
|
48
51
|
model: options.discovery.model,
|
|
49
|
-
translation
|
|
52
|
+
translation,
|
|
50
53
|
backReferences,
|
|
51
54
|
bigintBinding,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
};
|
|
56
|
+
const execute = sharedConnection === undefined
|
|
57
|
+
? createPrismaOperationExecutor({
|
|
58
|
+
...executorDependencies,
|
|
59
|
+
client: options.client,
|
|
60
|
+
scope: "root",
|
|
61
|
+
...transactionOptions,
|
|
62
|
+
}).execute
|
|
63
|
+
: (operation) => sharedConnection.plain(operation, (transactional) => createPrismaOperationExecutor({
|
|
64
|
+
...executorDependencies,
|
|
65
|
+
client: transactional,
|
|
66
|
+
scope: "transaction",
|
|
67
|
+
}).execute(operation));
|
|
68
|
+
const rootSql = createPrismaSqlStatementExecutor(options.client);
|
|
69
|
+
const executeSql = sharedConnection === undefined
|
|
70
|
+
? rootSql
|
|
71
|
+
: (statement) => sharedConnection.plain("ctx.$sql", (transactional) => createPrismaSqlStatementExecutor(transactional)(statement));
|
|
58
72
|
if (description.transactions === "none") {
|
|
59
73
|
return {
|
|
60
74
|
model,
|
|
@@ -68,12 +82,11 @@ export async function createPrismaAdapter(options) {
|
|
|
68
82
|
const transaction = createPrismaTransactionExecutor({
|
|
69
83
|
client: options.client,
|
|
70
84
|
model: options.discovery.model,
|
|
71
|
-
translation
|
|
85
|
+
translation,
|
|
72
86
|
backReferences,
|
|
73
87
|
bigintBinding,
|
|
74
|
-
...
|
|
75
|
-
|
|
76
|
-
: { transactionOptions: options.transactionOptions }),
|
|
88
|
+
...transactionOptions,
|
|
89
|
+
...(sharedConnection === undefined ? {} : { sharedConnection }),
|
|
77
90
|
});
|
|
78
91
|
return {
|
|
79
92
|
model,
|
|
@@ -35,6 +35,10 @@ export type AvPrismaProviderProfile = {
|
|
|
35
35
|
};
|
|
36
36
|
/** How a driver binds a `bigint` parameter: all 64 bits, or as a JavaScript safe integer. */
|
|
37
37
|
export type PrismaBigintBinding = "int64" | "safe-integer";
|
|
38
|
+
/** Whether an open interactive transaction holds the connection plain statements use. */
|
|
39
|
+
export type PrismaTransactionConnection = "shared" | "separate";
|
|
40
|
+
/** Whether the declared driver's open transaction holds its plain statements' connection. */
|
|
41
|
+
export declare function resolveTransactionConnection(driver: AvPrismaDriver): PrismaTransactionConnection;
|
|
38
42
|
/** How the declared driver binds a `bigint` parameter. */
|
|
39
43
|
export declare function resolveBigintBinding(driver: AvPrismaDriver): PrismaBigintBinding;
|
|
40
44
|
/**
|
|
@@ -14,6 +14,14 @@ const DRIVER_BIGINT_BINDING = {
|
|
|
14
14
|
"@prisma/adapter-pg": "int64",
|
|
15
15
|
"@prisma/adapter-d1": "safe-integer",
|
|
16
16
|
};
|
|
17
|
+
const DRIVER_TRANSACTION_CONNECTION = {
|
|
18
|
+
"@prisma/adapter-better-sqlite3": "shared",
|
|
19
|
+
"@prisma/adapter-pg": "separate",
|
|
20
|
+
"@prisma/adapter-d1": "separate",
|
|
21
|
+
};
|
|
22
|
+
export function resolveTransactionConnection(driver) {
|
|
23
|
+
return DRIVER_TRANSACTION_CONNECTION[driver];
|
|
24
|
+
}
|
|
17
25
|
export function resolveBigintBinding(driver) {
|
|
18
26
|
return DRIVER_BIGINT_BINDING[driver];
|
|
19
27
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aventara/prisma7-adapter",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.13",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "The Prisma 7 adapter for Aventara.",
|
|
6
6
|
"type": "module",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"CHANGELOG.md"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@aventara/core": "1.0.0-rc.
|
|
33
|
+
"@aventara/core": "1.0.0-rc.13"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@prisma/adapter-better-sqlite3": "^7.10.1",
|
|
@@ -95,9 +95,10 @@
|
|
|
95
95
|
"build": "node ../../scripts/build/build-output.cleaner.ts dist && node node_modules/typescript/bin/tsc -p tsconfig.json --removeComments --declaration false && node node_modules/typescript/bin/tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
|
|
96
96
|
"typecheck": "node node_modules/typescript/bin/tsc -p tsconfig.typecheck.json --noEmit",
|
|
97
97
|
"generate": "node node_modules/typescript/bin/tsc -p tsconfig.generate.json && node .generate/generate/discovery.generate.js && prisma generate --schema src/discovery/__fixtures__/ignore.prisma",
|
|
98
|
-
"db:push": "prisma db push --config prisma.config.sqlite.ts &&
|
|
99
|
-
"
|
|
100
|
-
"test
|
|
98
|
+
"db:push": "prisma db push --config prisma.config.sqlite.ts && pnpm run db:push:postgres",
|
|
99
|
+
"db:push:postgres": "sh -c 'if [ -n \"$AVENTARA_TEST_POSTGRES_URL\" ]; then prisma db push --config prisma.config.postgres.ts; else echo \"AVENTARA_TEST_POSTGRES_URL is unset: skipping the PostgreSQL schema push\"; fi'",
|
|
100
|
+
"test": "pnpm run generate && pnpm run db:push:postgres && vitest run --typecheck --typecheck.checker node_modules/typescript/bin/tsc --no-file-parallelism --config vitest.config.mts --root .",
|
|
101
|
+
"test:sqlite": "pnpm run generate && vitest run --config vitest.config.mts --root . src/environment.spec.ts",
|
|
101
102
|
"test:postgres": "pnpm run generate && prisma db push --config prisma.config.postgres.ts && vitest run --config vitest.config.mts --root . src/environment.spec.ts",
|
|
102
103
|
"lint": "biome lint src"
|
|
103
104
|
}
|