prisma-guard 1.23.0 → 1.25.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/README.md +126 -39
- package/dist/generator/index.js +312 -257
- package/dist/generator/index.js.map +1 -1
- package/dist/runtime/index.cjs +766 -403
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +65 -25
- package/dist/runtime/index.d.ts +65 -25
- package/dist/runtime/index.js +766 -403
- package/dist/runtime/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -453,6 +453,67 @@ take: { max: 100, default: 25 } // explicit max and default
|
|
|
453
453
|
take: { max: 100 } // max only, no default
|
|
454
454
|
```
|
|
455
455
|
|
|
456
|
+
### Unique where shapes
|
|
457
|
+
|
|
458
|
+
`findUnique`, `findUniqueOrThrow`, `update`, `delete`, and `upsert` use Prisma's `WhereUniqueInput` syntax. For these methods, unique fields are configured directly in the shape:
|
|
459
|
+
```ts
|
|
460
|
+
await prisma.project
|
|
461
|
+
.guard({
|
|
462
|
+
where: { id: true },
|
|
463
|
+
})
|
|
464
|
+
.update({
|
|
465
|
+
data: { title: 'Updated' },
|
|
466
|
+
where: { id: 'abc123' },
|
|
467
|
+
})
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Do not use filter operator objects such as `{ id: { equals: true } }` in unique where shapes. That syntax belongs to normal `WhereInput` filters used by methods such as `findMany`, `findFirst`, `count`, `updateMany`, and `deleteMany`.
|
|
471
|
+
|
|
472
|
+
For compound unique constraints, use Prisma's generated compound selector name:
|
|
473
|
+
```prisma
|
|
474
|
+
model ProjectMember {
|
|
475
|
+
tenantId String
|
|
476
|
+
userId String
|
|
477
|
+
|
|
478
|
+
@@unique([tenantId, userId])
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
```ts
|
|
483
|
+
await prisma.projectMember
|
|
484
|
+
.guard({
|
|
485
|
+
where: {
|
|
486
|
+
tenantId_userId: {
|
|
487
|
+
tenantId: true,
|
|
488
|
+
userId: true,
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
})
|
|
492
|
+
.update({
|
|
493
|
+
data: req.body.data,
|
|
494
|
+
where: {
|
|
495
|
+
tenantId_userId: {
|
|
496
|
+
tenantId: 'tenant_1',
|
|
497
|
+
userId: 'user_1',
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
})
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Named compound constraints use the configured name as the selector:
|
|
504
|
+
```prisma
|
|
505
|
+
@@unique([tenantId, slug], name: "project_slug_per_tenant")
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
```ts
|
|
509
|
+
where: {
|
|
510
|
+
project_slug_per_tenant: {
|
|
511
|
+
tenantId: true,
|
|
512
|
+
slug: true,
|
|
513
|
+
},
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
456
517
|
### Creates
|
|
457
518
|
```ts
|
|
458
519
|
await prisma.project
|
|
@@ -473,15 +534,15 @@ Fields with `@zod .default(...)` or `@zod .catch(...)` that are omitted from the
|
|
|
473
534
|
await prisma.project
|
|
474
535
|
.guard({
|
|
475
536
|
data: { title: true },
|
|
476
|
-
where: { id:
|
|
537
|
+
where: { id: true },
|
|
477
538
|
})
|
|
478
539
|
.update({
|
|
479
540
|
data: { title: 'New title' },
|
|
480
|
-
where: { id:
|
|
541
|
+
where: { id: 'abc123' },
|
|
481
542
|
})
|
|
482
543
|
```
|
|
483
544
|
|
|
484
|
-
In update mode, all `data` fields are optional. The `where` shape
|
|
545
|
+
In update mode, all `data` fields are optional. The `where` shape must use Prisma unique selector syntax for `update`.
|
|
485
546
|
|
|
486
547
|
### Forced values
|
|
487
548
|
|
|
@@ -519,12 +580,12 @@ Forced where conditions are conflict-checked during shape construction. If the s
|
|
|
519
580
|
```ts
|
|
520
581
|
await prisma.project
|
|
521
582
|
.guard({
|
|
522
|
-
where: { id:
|
|
583
|
+
where: { id: true },
|
|
523
584
|
})
|
|
524
|
-
.delete({ where: { id:
|
|
585
|
+
.delete({ where: { id: 'abc123' } })
|
|
525
586
|
```
|
|
526
587
|
|
|
527
|
-
`data` is not valid for delete shapes.
|
|
588
|
+
`data` is not valid for delete shapes. The `where` shape must use Prisma unique selector syntax for `delete`.
|
|
528
589
|
|
|
529
590
|
### Batch creates
|
|
530
591
|
```ts
|
|
@@ -689,6 +750,7 @@ await prisma.post
|
|
|
689
750
|
disconnect: { id: true },
|
|
690
751
|
},
|
|
691
752
|
},
|
|
753
|
+
where: { id: true },
|
|
692
754
|
})
|
|
693
755
|
.update({
|
|
694
756
|
data: {
|
|
@@ -698,7 +760,7 @@ await prisma.post
|
|
|
698
760
|
disconnect: [{ id: 'tag3' }],
|
|
699
761
|
},
|
|
700
762
|
},
|
|
701
|
-
where: { id:
|
|
763
|
+
where: { id: 'post1' },
|
|
702
764
|
})
|
|
703
765
|
```
|
|
704
766
|
|
|
@@ -736,6 +798,7 @@ await prisma.user
|
|
|
736
798
|
},
|
|
737
799
|
},
|
|
738
800
|
},
|
|
801
|
+
where: { id: true },
|
|
739
802
|
})
|
|
740
803
|
.update({
|
|
741
804
|
data: {
|
|
@@ -745,7 +808,7 @@ await prisma.user
|
|
|
745
808
|
connect: [{ id: 'existing-post-id' }],
|
|
746
809
|
},
|
|
747
810
|
},
|
|
748
|
-
where: { id:
|
|
811
|
+
where: { id: userId },
|
|
749
812
|
})
|
|
750
813
|
```
|
|
751
814
|
|
|
@@ -1069,7 +1132,7 @@ await prisma.project
|
|
|
1069
1132
|
await prisma.project
|
|
1070
1133
|
.guard({
|
|
1071
1134
|
data: { title: true },
|
|
1072
|
-
where: { id:
|
|
1135
|
+
where: { id: true },
|
|
1073
1136
|
select: {
|
|
1074
1137
|
id: true,
|
|
1075
1138
|
title: true,
|
|
@@ -1080,7 +1143,7 @@ await prisma.project
|
|
|
1080
1143
|
})
|
|
1081
1144
|
.update({
|
|
1082
1145
|
data: { title: 'Updated' },
|
|
1083
|
-
where: { id:
|
|
1146
|
+
where: { id: 'abc123' },
|
|
1084
1147
|
select: {
|
|
1085
1148
|
id: true,
|
|
1086
1149
|
title: true,
|
|
@@ -1095,11 +1158,11 @@ await prisma.project
|
|
|
1095
1158
|
```ts
|
|
1096
1159
|
await prisma.project
|
|
1097
1160
|
.guard({
|
|
1098
|
-
where: { id:
|
|
1161
|
+
where: { id: true },
|
|
1099
1162
|
include: { members: true },
|
|
1100
1163
|
})
|
|
1101
1164
|
.delete({
|
|
1102
|
-
where: { id:
|
|
1165
|
+
where: { id: 'abc123' },
|
|
1103
1166
|
include: { members: true },
|
|
1104
1167
|
})
|
|
1105
1168
|
```
|
|
@@ -1197,12 +1260,12 @@ Upsert is supported with dedicated `create` and `update` shape keys that mirror
|
|
|
1197
1260
|
```ts
|
|
1198
1261
|
await prisma.project
|
|
1199
1262
|
.guard({
|
|
1200
|
-
where: { id:
|
|
1263
|
+
where: { id: true },
|
|
1201
1264
|
create: { title: true, status: true },
|
|
1202
1265
|
update: { title: true },
|
|
1203
1266
|
})
|
|
1204
1267
|
.upsert({
|
|
1205
|
-
where: { id:
|
|
1268
|
+
where: { id: 'abc123' },
|
|
1206
1269
|
create: { title: 'New Project', status: 'active' },
|
|
1207
1270
|
update: { title: 'Updated Title' },
|
|
1208
1271
|
})
|
|
@@ -1214,7 +1277,7 @@ Upsert shapes must define all three: `where`, `create`, and `update`. Missing an
|
|
|
1214
1277
|
|
|
1215
1278
|
The `create` branch follows the same rules as regular create shapes: all required fields without defaults must be accounted for (as client-allowed, forced, scope FK, or `@zod .default(...)`/`@zod .catch(...)`). The `update` branch follows update rules: all fields are optional.
|
|
1216
1279
|
|
|
1217
|
-
The `where` must satisfy a unique constraint
|
|
1280
|
+
The `where` must satisfy a unique constraint using Prisma unique selector syntax, same as `update` and `delete`. Filter operator objects such as `{ id: { equals: true } }` are rejected in unique where shapes.
|
|
1218
1281
|
|
|
1219
1282
|
### All data shape value types work
|
|
1220
1283
|
```ts
|
|
@@ -1222,7 +1285,7 @@ import { force } from 'prisma-guard'
|
|
|
1222
1285
|
|
|
1223
1286
|
await prisma.project
|
|
1224
1287
|
.guard({
|
|
1225
|
-
where: { id:
|
|
1288
|
+
where: { id: true },
|
|
1226
1289
|
create: {
|
|
1227
1290
|
title: (base) => base.min(1).max(200),
|
|
1228
1291
|
status: 'draft',
|
|
@@ -1233,7 +1296,7 @@ await prisma.project
|
|
|
1233
1296
|
},
|
|
1234
1297
|
})
|
|
1235
1298
|
.upsert({
|
|
1236
|
-
where: { id:
|
|
1299
|
+
where: { id: 'abc123' },
|
|
1237
1300
|
create: { title: 'New Project' },
|
|
1238
1301
|
update: { title: 'Updated' },
|
|
1239
1302
|
})
|
|
@@ -1245,13 +1308,13 @@ Upsert returns a record and supports `select` and `include`:
|
|
|
1245
1308
|
```ts
|
|
1246
1309
|
await prisma.project
|
|
1247
1310
|
.guard({
|
|
1248
|
-
where: { id:
|
|
1311
|
+
where: { id: true },
|
|
1249
1312
|
create: { title: true, status: true },
|
|
1250
1313
|
update: { title: true },
|
|
1251
1314
|
select: { id: true, title: true, status: true },
|
|
1252
1315
|
})
|
|
1253
1316
|
.upsert({
|
|
1254
|
-
where: { id:
|
|
1317
|
+
where: { id: 'abc123' },
|
|
1255
1318
|
create: { title: 'New', status: 'active' },
|
|
1256
1319
|
update: { title: 'Updated' },
|
|
1257
1320
|
select: { id: true, title: true },
|
|
@@ -1274,18 +1337,18 @@ Upsert works with named shapes and context-dependent shapes:
|
|
|
1274
1337
|
await prisma.project
|
|
1275
1338
|
.guard({
|
|
1276
1339
|
'/admin/projects/:id': {
|
|
1277
|
-
where: { id:
|
|
1340
|
+
where: { id: true },
|
|
1278
1341
|
create: { title: true, status: true, priority: true },
|
|
1279
1342
|
update: { title: true, status: true, priority: true },
|
|
1280
1343
|
},
|
|
1281
1344
|
'/editor/projects/:id': {
|
|
1282
|
-
where: { id:
|
|
1345
|
+
where: { id: true },
|
|
1283
1346
|
create: { title: true, status: 'draft' },
|
|
1284
1347
|
update: { title: true },
|
|
1285
1348
|
},
|
|
1286
1349
|
}, req.headers['x-caller'])
|
|
1287
1350
|
.upsert({
|
|
1288
|
-
where: { id:
|
|
1351
|
+
where: { id: req.params.id },
|
|
1289
1352
|
create: req.body.create,
|
|
1290
1353
|
update: req.body.update,
|
|
1291
1354
|
})
|
|
@@ -1339,16 +1402,16 @@ await prisma.project
|
|
|
1339
1402
|
.guard({
|
|
1340
1403
|
'/admin/projects/:id': {
|
|
1341
1404
|
data: { title: true, status: true, priority: true },
|
|
1342
|
-
where: { id:
|
|
1405
|
+
where: { id: true },
|
|
1343
1406
|
},
|
|
1344
1407
|
'/editor/projects/:id': {
|
|
1345
1408
|
data: { title: true },
|
|
1346
|
-
where: { id:
|
|
1409
|
+
where: { id: true },
|
|
1347
1410
|
},
|
|
1348
1411
|
}, req.headers['x-caller'])
|
|
1349
1412
|
.update({
|
|
1350
1413
|
data: req.body.data,
|
|
1351
|
-
where: { id:
|
|
1414
|
+
where: { id: req.params.id },
|
|
1352
1415
|
})
|
|
1353
1416
|
```
|
|
1354
1417
|
|
|
@@ -1605,20 +1668,41 @@ If this behavior is not what you want, restructure your schema so the model refe
|
|
|
1605
1668
|
|
|
1606
1669
|
## findUnique behavior
|
|
1607
1670
|
|
|
1608
|
-
Prisma `findUnique` only
|
|
1671
|
+
Prisma `findUnique` and `findUniqueOrThrow` only accept declared unique selectors.
|
|
1609
1672
|
|
|
1610
|
-
This is valid:
|
|
1673
|
+
This is valid Prisma unique selector syntax:
|
|
1611
1674
|
```ts
|
|
1612
1675
|
await prisma.project.findUnique({
|
|
1613
|
-
where: { id:
|
|
1676
|
+
where: { id: projectId },
|
|
1614
1677
|
})
|
|
1615
1678
|
```
|
|
1616
1679
|
|
|
1617
|
-
|
|
1680
|
+
For compound unique constraints, Prisma uses a named selector object:
|
|
1681
|
+
```prisma
|
|
1682
|
+
model Project {
|
|
1683
|
+
tenantId String
|
|
1684
|
+
slug String
|
|
1685
|
+
|
|
1686
|
+
@@unique([tenantId, slug])
|
|
1687
|
+
}
|
|
1688
|
+
```
|
|
1689
|
+
|
|
1690
|
+
```ts
|
|
1691
|
+
await prisma.project.findUnique({
|
|
1692
|
+
where: {
|
|
1693
|
+
tenantId_slug: {
|
|
1694
|
+
tenantId,
|
|
1695
|
+
slug,
|
|
1696
|
+
},
|
|
1697
|
+
},
|
|
1698
|
+
})
|
|
1699
|
+
```
|
|
1700
|
+
|
|
1701
|
+
This flat where object is not valid for `findUnique` unless your Prisma schema declares a matching compound selector with that exact shape:
|
|
1618
1702
|
```ts
|
|
1619
1703
|
where: {
|
|
1620
|
-
id:
|
|
1621
|
-
tenantId
|
|
1704
|
+
id: projectId,
|
|
1705
|
+
tenantId,
|
|
1622
1706
|
}
|
|
1623
1707
|
```
|
|
1624
1708
|
|
|
@@ -1649,7 +1733,7 @@ This is weaker because:
|
|
|
1649
1733
|
|
|
1650
1734
|
For tenant isolation, `"reject"` is the safer production default.
|
|
1651
1735
|
|
|
1652
|
-
Guard shapes for `findUnique` and `findUniqueOrThrow` must define `where`. A shape without `where` for these methods throws `ShapeError`.
|
|
1736
|
+
Guard shapes for `findUnique` and `findUniqueOrThrow` must define `where`. A shape without `where` for these methods throws `ShapeError`. Unique where shapes must use Prisma unique selector syntax, for example `{ id: true }` or `{ tenantId_slug: { tenantId: true, slug: true } }`.
|
|
1653
1737
|
|
|
1654
1738
|
---
|
|
1655
1739
|
|
|
@@ -1771,16 +1855,20 @@ If a model references a scope root through composite foreign keys, that specific
|
|
|
1771
1855
|
|
|
1772
1856
|
Handle these models explicitly via shape rules.
|
|
1773
1857
|
|
|
1774
|
-
### Compound unique selectors
|
|
1775
|
-
|
|
1776
|
-
Guard currently records unique constraints as arrays of field names but does not generate the named compound selector schemas that Prisma uses for `@@unique` constraints. For example, `@@unique([firstName, lastName])` requires the selector `{ firstName_lastName: { firstName: "A", lastName: "B" } }`, but guard produces flat `{ firstName: "A", lastName: "B" }` output. This affects `findUnique`, `update`, `delete`, `upsert`, `connect`, and `connectOrCreate` with compound unique constraints.
|
|
1777
|
-
|
|
1778
|
-
Single-field unique constraints work correctly. Compound unique support is planned.
|
|
1779
|
-
|
|
1780
1858
|
### Cursor fields must cover a unique constraint
|
|
1781
1859
|
|
|
1782
1860
|
Prisma requires cursor-based pagination to use uniquely-identifiable fields. Guard enforces this at shape construction time: cursor fields must cover at least one unique constraint from the model. Non-unique cursor shapes are rejected with `ShapeError`.
|
|
1783
1861
|
|
|
1862
|
+
Compound cursor selectors use the same Prisma selector syntax as compound unique `where` values:
|
|
1863
|
+
```ts
|
|
1864
|
+
cursor: {
|
|
1865
|
+
tenantId_slug: {
|
|
1866
|
+
tenantId: true,
|
|
1867
|
+
slug: true,
|
|
1868
|
+
},
|
|
1869
|
+
}
|
|
1870
|
+
```
|
|
1871
|
+
|
|
1784
1872
|
### `@zod` on list fields applies to the array
|
|
1785
1873
|
|
|
1786
1874
|
`@zod` directives on list fields (e.g. `String[]`) apply to the `z.array(...)` schema, not to individual elements. For example, `.min(1)` on a `String[]` field enforces a minimum array length of 1, not a minimum string length per element.
|
|
@@ -2154,7 +2242,6 @@ Node 22
|
|
|
2154
2242
|
|
|
2155
2243
|
Possible future improvements:
|
|
2156
2244
|
|
|
2157
|
-
* compound unique selector support
|
|
2158
2245
|
* richer relation-level policies
|
|
2159
2246
|
* nested write scope enforcement helpers
|
|
2160
2247
|
* adapter integrations for SQL-backed runtimes
|