@rowstile/prisma 0.1.0-alpha.2 → 0.1.0-alpha.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.
- package/README.md +3 -1
- package/dist/index.js +34 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -57,7 +57,9 @@ Traps it handles:
|
|
|
57
57
|
transactions, from the first caller's context. `authz()` runs each `findUnique` in a transaction of its own,
|
|
58
58
|
and refuses `$transaction([...])` (use `$transaction(async (tx) => ...)`), so no query runs as someone else.
|
|
59
59
|
Use the client `$extends(authz())` returns, and only that one: the client it was made from is refused
|
|
60
|
-
whenever it is used.
|
|
60
|
+
whenever it is used, inside your own extensions too. An extension you add to the client `authz()` returns is
|
|
61
|
+
placed before it, so `authz()` stays the last one: each of your query hooks runs once for a call, and sees
|
|
62
|
+
the errors `authz()` makes (`Refused`, `NotFound`).
|
|
61
63
|
- **Inserts that read the row back** (`RETURNING`, Prisma's `create`) also need the select rule: Postgres's own
|
|
62
64
|
message comes back as `Refused` naming the select rule.
|
|
63
65
|
- **Prisma's promises are lazy**: `actingAs` and `job` await what they return inside their scope.
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Use the client `$extends(authz())` returns, and only that one: the client it was made from is refused
|
|
15
15
|
* whenever it is used (Prisma answers the findUnique calls of one tick with one query, signed in as whoever
|
|
16
|
-
* called first).
|
|
16
|
+
* called first). That holds inside the app's own extensions too, on either side of authz(): an extension added
|
|
17
|
+
* to the client authz() returns is placed before it, so authz() stays the last one, and every hook of the
|
|
18
|
+
* app's runs once for each call, outside what authz() lets through.
|
|
17
19
|
*/
|
|
18
20
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
19
21
|
import { Prisma } from "@prisma/client/extension";
|
|
@@ -106,6 +108,17 @@ export function signedIn(factory, options = {}) {
|
|
|
106
108
|
},
|
|
107
109
|
});
|
|
108
110
|
}
|
|
111
|
+
async function transactionOf(tx) {
|
|
112
|
+
let found;
|
|
113
|
+
const make = tx._createPrismaPromise;
|
|
114
|
+
if (typeof make === "function")
|
|
115
|
+
await make(async (t) => { found = t; });
|
|
116
|
+
if (found?.kind !== "itx") {
|
|
117
|
+
throw new Error("@rowstile/prisma: this version of Prisma starts its transactions another way; " +
|
|
118
|
+
"report it at https://github.com/rowstile/rowstile/issues with the version of @prisma/client");
|
|
119
|
+
}
|
|
120
|
+
return found;
|
|
121
|
+
}
|
|
109
122
|
/** The client extension: refusals and hidden rows as errors, and db.$authz. */
|
|
110
123
|
export function authz(options = {}) {
|
|
111
124
|
return Prisma.defineExtension((client) => {
|
|
@@ -151,8 +164,10 @@ export function authz(options = {}) {
|
|
|
151
164
|
return Object.values(v);
|
|
152
165
|
return undefined;
|
|
153
166
|
};
|
|
167
|
+
// through the client this returns (set below), whose last hook lets the query through
|
|
168
|
+
let self = raw;
|
|
154
169
|
const q = {
|
|
155
|
-
query: async (text, values = []) => ({ rows: await
|
|
170
|
+
query: async (text, values = []) => ({ rows: await self.$queryRawUnsafe(text, ...values) }),
|
|
156
171
|
};
|
|
157
172
|
const c = calls(q);
|
|
158
173
|
const $authz = {
|
|
@@ -176,9 +191,10 @@ export function authz(options = {}) {
|
|
|
176
191
|
try {
|
|
177
192
|
if (!internal?.transaction && model && (operation === "findUnique" || operation === "findUniqueOrThrow")) {
|
|
178
193
|
// Prisma answers the findUnique calls of one tick together, from the first caller's context: run each
|
|
179
|
-
// in an interactive transaction of its own, which begins (and signs in) in this caller's context
|
|
180
|
-
|
|
181
|
-
|
|
194
|
+
// in an interactive transaction of its own, which begins (and signs in) in this caller's context.
|
|
195
|
+
// What is left of the call (this hook is the last: Prisma's own work) runs in it; calling the model
|
|
196
|
+
// again on the transaction would run every hook of the app's a second time
|
|
197
|
+
return await extended(() => raw.$transaction(async (tx) => query(args, { ...internal, transaction: await transactionOf(tx) })));
|
|
182
198
|
}
|
|
183
199
|
return await extended(() => query(args));
|
|
184
200
|
}
|
|
@@ -202,7 +218,7 @@ export function authz(options = {}) {
|
|
|
202
218
|
client: { $authz },
|
|
203
219
|
});
|
|
204
220
|
// a second layer, so that $parent has the hooks above: transactions begin through this client too. It
|
|
205
|
-
// changes nothing in $transaction's type.
|
|
221
|
+
// changes nothing in $transaction's type, nor in $extends's.
|
|
206
222
|
// The transaction begins inside the mark, but the app's callback runs outside it: the callback is the app's
|
|
207
223
|
// code, and a client not extended with authz() is refused there as anywhere (its findUnique calls would be
|
|
208
224
|
// answered together). Queries through tx go through the transaction, which needs no mark. What the callback
|
|
@@ -215,8 +231,19 @@ export function authz(options = {}) {
|
|
|
215
231
|
? (tx) => through.exit(async () => await fn(tx)) : fn;
|
|
216
232
|
return extended(() => parent.$transaction(app, ...rest));
|
|
217
233
|
},
|
|
234
|
+
// An extension added to this client goes before authz(), which is then applied again: Prisma calls query
|
|
235
|
+
// hooks in the order they were added, so a hook added after authz()'s would run inside what it lets
|
|
236
|
+
// through, and the client authz() was made from would not be refused there. (A function is called with
|
|
237
|
+
// this client, as Prisma does: what it adds comes back here.)
|
|
238
|
+
$extends(extension) {
|
|
239
|
+
if (typeof extension === "function")
|
|
240
|
+
return extension(this);
|
|
241
|
+
return raw.$extends(extension).$extends(authz(options));
|
|
242
|
+
},
|
|
218
243
|
};
|
|
219
|
-
|
|
244
|
+
const out = hooks.$extends({ name: "rowstile-transactions", client: transactions });
|
|
245
|
+
self = out;
|
|
246
|
+
return out;
|
|
220
247
|
});
|
|
221
248
|
}
|
|
222
249
|
function isScalar(v) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rowstile/prisma",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.4",
|
|
4
4
|
"description": "rowstile with Prisma: a client extension that signs every query in, and turns refusals into errors",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": { "type": "git", "url": "git+https://github.com/rowstile/rowstile.git", "directory": "sdk/typescript/prisma" },
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
"exports": { ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" } },
|
|
11
11
|
"files": ["dist"],
|
|
12
12
|
"sideEffects": false,
|
|
13
|
-
"dependencies": { "@rowstile/client": "0.1.0-alpha.
|
|
13
|
+
"dependencies": { "@rowstile/client": "0.1.0-alpha.4" },
|
|
14
14
|
"peerDependencies": { "@prisma/client": ">=7" }
|
|
15
15
|
}
|