@cerebruminc/yates 3.8.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +313 -70
- package/dist/expressions.d.ts +4 -5
- package/dist/expressions.js +0 -438
- package/dist/expressions.js.map +1 -1
- package/dist/index.d.ts +8 -51
- package/dist/index.js +1290 -789
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.0.0](https://github.com/cerebruminc/yates/compare/v3.8.1...v4.0.0) (2026-02-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* refactor permissions to query filters
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* refactor permissions to query filters ([06d1007](https://github.com/cerebruminc/yates/commit/06d10079c460e15a787516d9301daf28ce776c20))
|
|
13
|
+
|
|
3
14
|
## [3.8.1](https://github.com/cerebruminc/yates/compare/v3.8.0...v3.8.1) (2025-11-15)
|
|
4
15
|
|
|
5
16
|
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
[](https://www.npmjs.com/package/@cerebruminc/yates)
|
|
5
5
|
|
|
6
|
-
<h1>Yates = Prisma +
|
|
6
|
+
<h1>Yates = Prisma + Ability Filters</h1>
|
|
7
7
|
|
|
8
8
|
<p>
|
|
9
9
|
A module for implementing role-based access control with Prisma when using Postgres
|
|
@@ -15,11 +15,13 @@
|
|
|
15
15
|
|
|
16
16
|
<br>
|
|
17
17
|
|
|
18
|
-
Yates is a module for implementing role-based access control with Prisma. It is designed to be used with the [Prisma Client](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client) and
|
|
18
|
+
Yates is a module for implementing role-based access control with Prisma. It is designed to be used with the [Prisma Client](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client) and PostgreSQL. It applies role abilities directly to Prisma queries by injecting permission filters into the `where` clause and recursing through nested operations.
|
|
19
|
+
|
|
20
|
+
In practice, Yates builds a permission filter from the current user's role and abilities, ORs the abilities together per model + operation, and ANDs that filter with the original query. This means permissions are enforced before the query reaches the database.
|
|
19
21
|
|
|
20
22
|
## Prerequisites
|
|
21
23
|
|
|
22
|
-
Yates requires the `prisma` package at version 4.9.0 or greater and the `@prisma/client` package at version 4.0.0 or greater. Additionally, it uses [Prisma Client extensions](https://www.prisma.io/docs/concepts/components/prisma-client/client-extensions) to
|
|
24
|
+
Yates requires the `prisma` package at version 4.9.0 or greater and the `@prisma/client` package at version 4.0.0 or greater. Additionally, it uses [Prisma Client extensions](https://www.prisma.io/docs/concepts/components/prisma-client/client-extensions) to apply ability filters (which require a preview feature flag until Prisma 4.16.0, so you might need to enable this feature in your Prisma schema):
|
|
23
25
|
|
|
24
26
|
```prisma
|
|
25
27
|
generator client {
|
|
@@ -36,15 +38,22 @@ npm i @cerebruminc/yates
|
|
|
36
38
|
|
|
37
39
|
## Usage
|
|
38
40
|
|
|
39
|
-
Once you've installed Yates, you can use it in your Prisma project by importing it and calling the `setup` function. This function takes a Prisma Client instance and a configuration object as arguments and returns a client that can intercept all queries and apply the appropriate
|
|
41
|
+
Once you've installed Yates, you can use it in your Prisma project by importing it and calling the `setup` function. This function takes a Prisma Client instance and a configuration object as arguments and returns a client that can intercept all queries and apply the appropriate ability filters to them.
|
|
40
42
|
|
|
41
|
-
Yates uses [Prisma Client Extensions](https://www.prisma.io/docs/concepts/components/prisma-client/client-extensions) to
|
|
43
|
+
Yates uses [Prisma Client Extensions](https://www.prisma.io/docs/concepts/components/prisma-client/client-extensions) to apply ability filters to Prisma Client queries. This means that you can use the Prisma Client as you normally would, and Yates will automatically apply the appropriate filters to each query. It also means that you will need to apply your [Prisma Client middleware](https://www.prisma.io/docs/orm/prisma-client/client-extensions/middleware) _before_ creating the Yates client, as middleware cannot be applied to an extended client.
|
|
42
44
|
|
|
43
45
|
Client extensions share the same API as the Prisma Client, you can use the Yates client as a drop-in replacement for the Prisma Client in your application. They also share the same connection pool as the base client, which means that you can freely create new Yates clients with minimal performance impact.
|
|
44
46
|
|
|
45
|
-
The `setup` function will generate CRUD abilities for each model in your Prisma schema, as well as any additional abilities that you have defined in your configuration. It will then
|
|
47
|
+
The `setup` function will generate CRUD abilities for each model in your Prisma schema, as well as any additional abilities that you have defined in your configuration. It will then map those abilities to your user roles and apply the resulting filters to each Prisma query.
|
|
48
|
+
|
|
49
|
+
For Yates to be able to apply the correct abilities for each request, you must pass a function called `getContext` in the `setup` configuration that will return the user role for the current request. This function will be called for each request and the user role returned will be used to apply ability filters. If you want to bypass permissions completely for a specific role, you can return `null` from the `getContext` function for that role.
|
|
50
|
+
|
|
51
|
+
### Nested relations
|
|
52
|
+
|
|
53
|
+
Yates applies permissions recursively across nested relations:
|
|
46
54
|
|
|
47
|
-
|
|
55
|
+
- **Reads (`include`/`select`)**: Yates walks the selection tree and injects read filters for each related model. If the role has no read ability for a related model, the selection is dropped.
|
|
56
|
+
- **Writes (nested create/update/delete/upsert)**: Yates validates each nested operation against the related model's abilities. For example, nested creates are checked against insert filters, and nested updates/deletes verify the target record is permitted before executing.
|
|
48
57
|
|
|
49
58
|
For accessing the context of a Prisma query, we recommend using a package like [cls-hooked](https://www.npmjs.com/package/cls-hooked) to store the context in the current session.
|
|
50
59
|
|
|
@@ -59,46 +68,35 @@ const prisma = new PrismaClient();
|
|
|
59
68
|
const client = await setup({
|
|
60
69
|
prisma,
|
|
61
70
|
// Define any custom abilities that you want to add to the system.
|
|
62
|
-
customAbilities:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
operation: "INSERT",
|
|
75
|
-
},
|
|
71
|
+
customAbilities: {
|
|
72
|
+
Post: {
|
|
73
|
+
insertOwnPost: {
|
|
74
|
+
description: "Insert own post",
|
|
75
|
+
// You can express the rule as a Prisma `where` clause.
|
|
76
|
+
expression: (_client, _row, context) => ({
|
|
77
|
+
// This expression uses a context setting returned by the getContext function
|
|
78
|
+
authorId: context('user.id')
|
|
79
|
+
}),
|
|
80
|
+
operation: "INSERT",
|
|
76
81
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
},
|
|
82
|
+
deleteOwnPost: {
|
|
83
|
+
description: "Delete own post",
|
|
84
|
+
expression: (_client, _row, context) => ({
|
|
85
|
+
authorId: context("user.id")
|
|
86
|
+
}),
|
|
87
|
+
operation: "DELETE",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
User: {
|
|
91
|
+
updateOwnUser: {
|
|
92
|
+
description: "Update own user",
|
|
93
|
+
expression: (_client, _row, context) => ({
|
|
94
|
+
id: context("user.id")
|
|
95
|
+
}),
|
|
96
|
+
operation: "UPDATE",
|
|
91
97
|
},
|
|
92
|
-
User: {
|
|
93
|
-
updateOwnUser: {
|
|
94
|
-
description: "Update own user",
|
|
95
|
-
// For low-level control you can also write expressions as a raw SQL string.
|
|
96
|
-
expression: `current_setting('user.id') = "id"`,
|
|
97
|
-
operation: "UPDATE",
|
|
98
|
-
},
|
|
99
|
-
}
|
|
100
98
|
}
|
|
101
|
-
}
|
|
99
|
+
},
|
|
102
100
|
// Return a mapping of user roles and abilities.
|
|
103
101
|
// This function is parameterised with a list of all CRUD abilities that have been
|
|
104
102
|
// automatically generated by Yates, as well as any customAbilities that have been defined.
|
|
@@ -107,7 +105,10 @@ const client = await setup({
|
|
|
107
105
|
SUPER_ADMIN: "*",
|
|
108
106
|
USER: [
|
|
109
107
|
abilities.User.read,
|
|
110
|
-
abilities.
|
|
108
|
+
abilities.Post.read,
|
|
109
|
+
abilities.Post.insertOwnPost,
|
|
110
|
+
abilities.Post.deleteOwnPost,
|
|
111
|
+
abilities.User.updateOwnUser,
|
|
111
112
|
],
|
|
112
113
|
};
|
|
113
114
|
},
|
|
@@ -124,17 +125,11 @@ const client = await setup({
|
|
|
124
125
|
return {
|
|
125
126
|
role,
|
|
126
127
|
context: {
|
|
127
|
-
// This context setting will be available in ability expressions
|
|
128
|
-
|
|
128
|
+
// This context setting will be available in ability expressions via `context(...)`
|
|
129
|
+
"user.id": user.id,
|
|
129
130
|
},
|
|
130
131
|
};
|
|
131
132
|
},
|
|
132
|
-
options: {
|
|
133
|
-
// The maximum amount of time Yates will wait to acquire a transaction from the database. The default value is 30 seconds.
|
|
134
|
-
txMaxWait: 5000,
|
|
135
|
-
// The maximum amount of time the Yates query transaction can run before being canceled and rolled back. The default value is 30 seconds.
|
|
136
|
-
txTimeout: 10000,
|
|
137
|
-
}
|
|
138
133
|
});
|
|
139
134
|
```
|
|
140
135
|
|
|
@@ -145,35 +140,283 @@ const client = await setup({
|
|
|
145
140
|
When defining an ability you need to provide the following properties:
|
|
146
141
|
|
|
147
142
|
- `description`: A description of the ability.
|
|
148
|
-
- `expression`: A
|
|
149
|
-
- For `INSERT
|
|
150
|
-
- For `SELECT` operations, the expression
|
|
151
|
-
- `operation`: The operation that the ability is being applied to. This can be one of `
|
|
143
|
+
- `expression`: A Prisma `where` clause (or a function that returns one) that will be combined with the original query. Abilities for the same model + operation are OR-ed together, and the resulting filter is AND-ed with the original query.
|
|
144
|
+
- For `INSERT` operations, the expression is matched against the incoming `data`.
|
|
145
|
+
- For `SELECT`, `UPDATE` and `DELETE` operations, the expression is merged into the Prisma `where` clause.
|
|
146
|
+
- `operation`: The operation that the ability is being applied to. This can be one of `INSERT`, `SELECT`, `UPDATE` or `DELETE`.
|
|
152
147
|
|
|
153
148
|
### Debug
|
|
154
149
|
|
|
155
150
|
To run Yates in debug mode, use the environment variable `DEBUG=yates`.
|
|
156
151
|
|
|
157
|
-
##
|
|
152
|
+
## Local development database
|
|
153
|
+
|
|
154
|
+
### Start/stop Postgres via Docker
|
|
155
|
+
|
|
156
|
+
This repo includes a local Postgres service in `docker-compose.yml` (mapped to port **5666** on your host):
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
docker compose up -d db
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
To stop it:
|
|
158
163
|
|
|
159
|
-
|
|
164
|
+
```bash
|
|
165
|
+
docker compose down
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Initialize the databases with Prisma
|
|
169
|
+
|
|
170
|
+
Run the setup script against the Docker database:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
DATABASE_URL="postgresql://postgres:postgres@localhost:5666/yates" \
|
|
174
|
+
DATABASE_URL_2="postgresql://postgres:postgres@localhost:5666/yates_2" \
|
|
175
|
+
npm run setup
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Benchmarks
|
|
179
|
+
|
|
180
|
+
This repo includes a simple in-process benchmark harness to compare performance across branches (v1 vs v2).
|
|
181
|
+
|
|
182
|
+
### Seed data
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npm run bench:seed
|
|
186
|
+
```
|
|
160
187
|
|
|
161
|
-
|
|
162
|
-
|
|
188
|
+
Optional size overrides:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
BENCH_USERS=5000 BENCH_POSTS=100000 BENCH_TAGS=200 npm run bench:seed
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Run benchmarks
|
|
195
|
+
|
|
196
|
+
Build first, then run:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm run build
|
|
200
|
+
npm run bench:run
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Benchmark controls:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
BENCH_ITERS=100 BENCH_WARMUP=10 npm run bench:run
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Comparing v1 vs v2
|
|
210
|
+
|
|
211
|
+
Run the same commands on each branch and compare the JSON output:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
git checkout master # v1 (RLS)
|
|
215
|
+
npm run build
|
|
216
|
+
npm run bench:run > bench-v1.json
|
|
217
|
+
|
|
218
|
+
git checkout lucianbuzzo/v2 # v2 (query filters)
|
|
219
|
+
npm run build
|
|
220
|
+
npm run bench:run > bench-v2.json
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Cookbook
|
|
224
|
+
|
|
225
|
+
Common permission patterns expressed as Yates abilities.
|
|
226
|
+
|
|
227
|
+
### Read own records (user-scoped)
|
|
228
|
+
|
|
229
|
+
Allow a user to read only their own records:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
customAbilities: {
|
|
233
|
+
Post: {
|
|
234
|
+
readOwnPosts: {
|
|
235
|
+
description: "Read own posts",
|
|
236
|
+
operation: "SELECT",
|
|
237
|
+
expression: (_client, _row, context) => ({
|
|
238
|
+
authorId: context("user.id") as string,
|
|
239
|
+
}),
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
getRoles: (abilities) => ({
|
|
244
|
+
USER: [abilities.Post.readOwnPosts],
|
|
245
|
+
}),
|
|
246
|
+
```
|
|
163
247
|
|
|
164
|
-
###
|
|
248
|
+
### Create only if the record is owned by the user
|
|
249
|
+
|
|
250
|
+
Allow creates only when `authorId` matches the current user:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
customAbilities: {
|
|
254
|
+
Post: {
|
|
255
|
+
createOwnPosts: {
|
|
256
|
+
description: "Create own posts",
|
|
257
|
+
operation: "INSERT",
|
|
258
|
+
expression: (_client, _row, context) => ({
|
|
259
|
+
authorId: context("user.id") as string,
|
|
260
|
+
}),
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Read public OR owned
|
|
267
|
+
|
|
268
|
+
Combine multiple abilities; they are OR-ed together:
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
customAbilities: {
|
|
272
|
+
Post: {
|
|
273
|
+
readPublic: {
|
|
274
|
+
description: "Read public posts",
|
|
275
|
+
operation: "SELECT",
|
|
276
|
+
expression: () => ({ published: true }),
|
|
277
|
+
},
|
|
278
|
+
readOwn: {
|
|
279
|
+
description: "Read own posts",
|
|
280
|
+
operation: "SELECT",
|
|
281
|
+
expression: (_client, _row, context) => ({
|
|
282
|
+
authorId: context("user.id") as string,
|
|
283
|
+
}),
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
getRoles: (abilities) => ({
|
|
288
|
+
USER: [abilities.Post.readPublic, abilities.Post.readOwn],
|
|
289
|
+
}),
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Update only owned records
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
customAbilities: {
|
|
296
|
+
Post: {
|
|
297
|
+
updateOwn: {
|
|
298
|
+
description: "Update own posts",
|
|
299
|
+
operation: "UPDATE",
|
|
300
|
+
expression: (_client, _row, context) => ({
|
|
301
|
+
authorId: context("user.id") as string,
|
|
302
|
+
}),
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Delete only if record is unpublished and owned
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
customAbilities: {
|
|
312
|
+
Post: {
|
|
313
|
+
deleteOwnDrafts: {
|
|
314
|
+
description: "Delete own drafts",
|
|
315
|
+
operation: "DELETE",
|
|
316
|
+
expression: (_client, _row, context) => ({
|
|
317
|
+
AND: [
|
|
318
|
+
{ authorId: context("user.id") as string },
|
|
319
|
+
{ published: false },
|
|
320
|
+
],
|
|
321
|
+
}),
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Tenant isolation by organization id
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
customAbilities: {
|
|
331
|
+
Organization: {
|
|
332
|
+
readOrg: {
|
|
333
|
+
description: "Read org",
|
|
334
|
+
operation: "SELECT",
|
|
335
|
+
expression: (_client, _row, context) => ({
|
|
336
|
+
id: context("org.id") as string,
|
|
337
|
+
}),
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
Post: {
|
|
341
|
+
readOrgPosts: {
|
|
342
|
+
description: "Read org posts",
|
|
343
|
+
operation: "SELECT",
|
|
344
|
+
expression: (_client, _row, context) => ({
|
|
345
|
+
organizationId: context("org.id") as string,
|
|
346
|
+
}),
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Membership-based access (relation filter)
|
|
353
|
+
|
|
354
|
+
Allow access when the user has a role in the org (relation filter):
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
customAbilities: {
|
|
358
|
+
Organization: {
|
|
359
|
+
readOrgIfMember: {
|
|
360
|
+
description: "Read org if member",
|
|
361
|
+
operation: "SELECT",
|
|
362
|
+
expression: (_client, _row, context) => ({
|
|
363
|
+
roleAssignment: {
|
|
364
|
+
some: {
|
|
365
|
+
userId: context("user.id") as string,
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
}),
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Admin bypass
|
|
375
|
+
|
|
376
|
+
Grant all abilities for admins:
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
getRoles: (abilities) => ({
|
|
380
|
+
ADMIN: "*",
|
|
381
|
+
USER: [abilities.Post.read, abilities.Post.create],
|
|
382
|
+
}),
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Soft delete visibility
|
|
386
|
+
|
|
387
|
+
Hide soft-deleted records by default:
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
customAbilities: {
|
|
391
|
+
Post: {
|
|
392
|
+
readNotDeleted: {
|
|
393
|
+
description: "Read non-deleted posts",
|
|
394
|
+
operation: "SELECT",
|
|
395
|
+
expression: () => ({
|
|
396
|
+
deletedAt: null,
|
|
397
|
+
}),
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Tradeoffs vs RLS
|
|
404
|
+
|
|
405
|
+
Yates enforces permissions in the application layer by injecting filters into Prisma queries. Compared to database-level RLS, there are some tradeoffs:
|
|
406
|
+
|
|
407
|
+
- **No DB-level enforcement if Prisma is bypassed**: direct SQL or other clients won’t be protected unless you keep RLS in place.
|
|
408
|
+
- **Extra queries for some create checks**: relation-based create checks may trigger preflight reads to verify related records.
|
|
409
|
+
- **Requires consistent usage**: permissions apply only when using the Yates-wrapped Prisma client.
|
|
410
|
+
|
|
411
|
+
## Known limitations
|
|
165
412
|
|
|
166
|
-
|
|
413
|
+
### Expression limits
|
|
167
414
|
|
|
168
|
-
- `AND`
|
|
169
|
-
- `OR`
|
|
170
|
-
- `NOT`
|
|
171
|
-
- `is`
|
|
172
|
-
- `isNot`
|
|
415
|
+
- Create checks support scalar filters and basic `AND`/`OR`/`NOT` logic. Relation filters are supported when the related record can be resolved from the create `data` (for example via `connect` or foreign key fields).
|
|
173
416
|
|
|
174
|
-
|
|
417
|
+
## Migration
|
|
175
418
|
|
|
176
|
-
|
|
419
|
+
- v1 -> v2 guide: `MIGRATION.md`
|
|
177
420
|
|
|
178
421
|
## License
|
|
179
422
|
|
package/dist/expressions.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { Prisma, PrismaClient } from "@prisma/client";
|
|
2
2
|
import { defineDmmfProperty } from "@prisma/client/runtime/library";
|
|
3
|
-
import { AsyncReturnType } from "type-fest";
|
|
4
3
|
export type RuntimeDataModel = Parameters<typeof defineDmmfProperty>[1];
|
|
5
4
|
type FFMeta<M extends Prisma.ModelName> = PrismaClient[Uncapitalize<M>]["findFirst"];
|
|
6
5
|
type ModelWhereArgs<M extends Prisma.ModelName> = Exclude<Parameters<FFMeta<M>>["0"], undefined>["where"];
|
|
7
|
-
type
|
|
8
|
-
type
|
|
9
|
-
export type
|
|
10
|
-
export
|
|
6
|
+
type ModelFieldRefs<M extends Prisma.ModelName> = PrismaClient[Uncapitalize<M>]["fields"];
|
|
7
|
+
export type ExpressionContext<ContextKeys extends string> = (key: ContextKeys) => string | number | string[] | undefined;
|
|
8
|
+
export type ExpressionRow<M extends Prisma.ModelName> = <K extends keyof ModelFieldRefs<M>>(col: K) => ModelFieldRefs<M>[K];
|
|
9
|
+
export type Expression<ContextKeys extends string, M extends Prisma.ModelName> = ModelWhereArgs<M> | ((client: PrismaClient, row: ExpressionRow<M>, context: ExpressionContext<ContextKeys>) => ModelWhereArgs<M> | Promise<ModelWhereArgs<M>>);
|
|
11
10
|
export {};
|