@casl/prisma
Advanced tools
Comparing version
@@ -1,2 +0,2 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:true});var t=require("@ucast/core");var e=require("@ucast/js");var r=require("@casl/ability/extra");var n=require("@casl/ability");class s extends Error{static invalidArgument(t,e,r){const n=`${typeof e}(${JSON.stringify(e,null,2)})`;return new this(`"${t}" expects to receive ${r} but instead got "${n}"`)}}const o=t=>t&&(t.constructor===Object||!t.constructor);const a={type:"field",validate(t,e){if(Array.isArray(e)||o(e))throw new s(`"${t.name}" does not supports comparison of arrays and objects`)}};const i={type:"field",parse(e,r,{hasOperators:n,field:a,parse:i}){if(o(r)&&!n(r)||Array.isArray(r))throw new s(`"${e.name}" does not supports comparison of arrays and objects`);if(!o(r))return new t.FieldCondition("notEquals",a,r);return new t.CompoundCondition("NOT",[i(r,{field:a})])}};const c={type:"field",validate(t,e){if(!Array.isArray(e))throw s.invalidArgument(t.name,e,"an array")}};const u={type:"field",validate(t,e){const r=typeof e;const n="string"===r||"number"===r&&Number.isFinite(e)||e instanceof Date;if(!n)throw s.invalidArgument(t.name,e,"comparable value")}};const l=new Set(["insensitive","default"]);const p={type:"field",validate(t,e){if(!l.has(e))throw s.invalidArgument(t.name,e,`one of ${Array.from(l).join(", ")}`)},parse:()=>t.NULL_CONDITION};const f={type:"field",validate(t,e){if("string"!==typeof e)throw s.invalidArgument(t.name,e,"string")},parse(e,r,{query:n,field:s}){const o="insensitive"===n.mode?`i${e.name}`:e.name;return new t.FieldCondition(o,s,r)}};const y={type:"compound",validate(t,e){if(!e||"object"!==typeof e)throw s.invalidArgument(t.name,e,"an array or object")},parse(e,r,{parse:n}){const s=Array.isArray(r)?r:[r];const o=s.map((t=>n(t)));return new t.CompoundCondition(e.name,o)}};const d={type:"field",validate(t,e){if("boolean"!==typeof e)throw s.invalidArgument(t.name,e,"a boolean")}};const h={type:"field"};const w={type:"field",validate(t,e){if(!Array.isArray(e))throw s.invalidArgument(t.name,e,"an array")}};const b={type:"field",parse(e,r,{field:n,parse:a}){if(!o(r))throw s.invalidArgument(e.name,r,"a query for nested relation");return new t.FieldCondition(e.name,n,a(r))}};const g=(e,r)=>{const n=r.parse;if(!n)return Object.assign({},r,{parse(r,n,s){return new t.CompoundCondition("NOT",[new t.FieldCondition(e,s.field,n)])}});return Object.assign({},r,{parse(r,s,o){const a=n(r,s,o);if(a.operator!==r.name)throw new Error(`Cannot invert "${e}" operator parser because it returns a complex Condition`);a.operator=e;return new t.CompoundCondition("NOT",[a])}})};const v={equals:a,not:i,in:c,notIn:g("in",c),lt:u,lte:u,gt:u,gte:u,mode:p,startsWith:f,endsWith:f,contains:f,isEmpty:d,has:h,hasSome:w,hasEvery:w,NOT:y,AND:y,OR:y,every:b,some:b,none:g("some",b),is:b,isNot:g("is",b)};class m extends t.ObjectQueryParser{constructor(){super(v,{defaultOperatorName:"equals"})}parse(e,r){if(r&&r.field)return t.buildAnd(this.parseFieldOperators(r.field,e));return super.parse(e)}}const A=(t,e,{get:r})=>r(e,t.field).startsWith(t.value);const O=(t,e,{get:r})=>r(e,t.field).toLowerCase().startsWith(t.value.toLowerCase());const j=(t,e,{get:r})=>r(e,t.field).endsWith(t.value);const x=(t,e,{get:r})=>r(e,t.field).toLowerCase().endsWith(t.value.toLowerCase());const N=(t,e,{get:r})=>r(e,t.field).includes(t.value);const $=(t,e,{get:r})=>r(e,t.field).toLowerCase().includes(t.value.toLowerCase());const q=(t,e,{get:r})=>{const n=r(e,t.field);const s=Array.isArray(n)&&0===n.length;return s===t.value};const E=(t,e,{get:r})=>{const n=r(e,t.field);return Array.isArray(n)&&n.includes(t.value)};const T=(t,e,{get:r})=>{const n=r(e,t.field);return Array.isArray(n)&&t.value.some((t=>n.includes(t)))};const W=(t,e,{get:r})=>{const n=r(e,t.field);return Array.isArray(n)&&t.value.every((t=>n.includes(t)))};const S=(t,e,{get:r,interpret:n})=>{const s=r(e,t.field);return Array.isArray(s)&&s.length>0&&s.every((e=>n(t.value,e)))};const _=(t,e,{get:r,interpret:n})=>{const s=r(e,t.field);return Array.isArray(s)&&s.some((e=>n(t.value,e)))};const D=(t,e,{get:r,interpret:n})=>{const s=r(e,t.field);return s&&"object"===typeof s&&n(t.value,s)};const M=(t,e,{interpret:r})=>t.value.every((t=>!r(t,e)));function P(t){return t&&"object"===typeof t?t.valueOf():t}const C=(t,r)=>e.compare(P(t),P(r));const I=e.createJsInterpreter({equals:e.eq,notEquals:e.ne,in:e.within,lt:e.lt,lte:e.lte,gt:e.gt,gte:e.gte,startsWith:A,istartsWith:O,endsWith:j,iendsWith:x,contains:N,icontains:$,isEmpty:q,has:E,hasSome:T,hasEvery:W,and:e.and,or:e.or,AND:e.and,OR:e.or,NOT:M,every:S,some:_,is:D},{get:(t,e)=>t[e],compare:C});const R=new m;const J=t.createTranslatorFactory(R.parse,I);function k(t){return t.inverted?{NOT:t.conditions}:t.conditions}const z={get(t,e){const s=r.rulesToQuery(t.t,t.o,e,k);if(null===s){const r=n.ForbiddenError.from(t.t).setMessage(`It's not allowed to run "${t.o}" on "${e}"`);r.action=t.o;r.subjectType=r.subject=e;throw r}const o=Object.create(null);if(s.$or)o.OR=s.$or;if(s.$and)o.AND=s.$and;return o}};function B(t,e){return new Proxy({t:t,o:e},z)}function F(t,e="read"){return B(t,e)}class PrismaAbility extends n.PureAbility{constructor(t,e){super(t,Object.assign({conditionsMatcher:J,fieldMatcher:n.fieldPatternMatcher},e))}}exports.ParsingQueryError=s;exports.PrismaAbility=PrismaAbility;exports.accessibleBy=F;exports.prismaQuery=J; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:true});var e=require("@casl/ability");var r=require("./runtime");const t=r.createAbilityFactory();const u=r.createAccessibleByFactory();class PrismaAbility extends e.PureAbility{constructor(t,u){super(t,Object.assign({conditionsMatcher:r.prismaQuery,fieldMatcher:e.fieldPatternMatcher},u))}}Object.defineProperty(exports,"Model",{enumerable:true,get:function(){return r.Model}});Object.defineProperty(exports,"ParsingQueryError",{enumerable:true,get:function(){return r.ParsingQueryError}});Object.defineProperty(exports,"Subjects",{enumerable:true,get:function(){return r.Subjects}});Object.defineProperty(exports,"prismaQuery",{enumerable:true,get:function(){return r.prismaQuery}});exports.PrismaAbility=PrismaAbility;exports.accessibleBy=u;exports.createPrismaAbility=t; | ||
//# sourceMappingURL=index.js.map |
@@ -1,5 +0,14 @@ | ||
export { prismaQuery } from './prisma/PrismaQuery'; | ||
export type { PrismaQuery, Model, Subjects } from './prisma/PrismaQuery'; | ||
export { accessibleBy } from './accessibleBy'; | ||
export { PrismaAbility } from './PrismaAbility'; | ||
export { ParsingQueryError } from './errors/ParsingQueryError'; | ||
import { AbilityOptions, AbilityTuple, PureAbility, RawRuleFrom } from '@casl/ability'; | ||
import { WhereInputPerModel, ModelName, PrismaQuery } from './prismaClientBoundTypes'; | ||
export type { PrismaQuery } from './prismaClientBoundTypes'; | ||
export { prismaQuery, Model, Subjects, ParsingQueryError } from './runtime'; | ||
declare const createPrismaAbility: <A extends AbilityTuple<string, import("@casl/ability").Subject> = [string, import("@prisma/client").Prisma.ModelName], C extends PrismaQuery<Record<string, any> & import("@casl/ability").ForcedSubject<string>> = PrismaQuery<Record<string, any> & import("@casl/ability").ForcedSubject<string>>>(rules?: (import("@casl/ability/dist/types/types").ToAbilityTypes<A> extends infer T ? T extends import("@casl/ability/dist/types/types").ToAbilityTypes<A> ? T extends import("@casl/ability/dist/types/types").AbilityTupleType<string, import("@casl/ability").SubjectType> ? import("@casl/ability").SubjectRawRule<T[0], T[1], C> : import("@casl/ability").ClaimRawRule<Extract<import("@casl/ability/dist/types/types").ToAbilityTypes<A>, string>> : never : never)[] | undefined, options?: AbilityOptions<A, C> | undefined) => PureAbility<A, C>; | ||
declare const accessibleBy: (ability: PureAbility<any, PrismaQuery<Record<string, any> & import("@casl/ability").ForcedSubject<string>>>, action?: string) => WhereInputPerModel; | ||
export { createPrismaAbility, accessibleBy, }; | ||
declare type ExtendedAbilityTuple<T extends AbilityTuple> = [T[0], 'all' | T[1]]; | ||
/** | ||
* @deprecated use createPrismaAbility instead | ||
*/ | ||
export declare class PrismaAbility<A extends AbilityTuple = [string, ModelName], C extends PrismaQuery = PrismaQuery> extends PureAbility<ExtendedAbilityTuple<A>, C> { | ||
constructor(rules?: RawRuleFrom<ExtendedAbilityTuple<A>, C>[], options?: AbilityOptions<ExtendedAbilityTuple<A>, C>); | ||
} |
{ | ||
"name": "@casl/prisma", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Allows to query accessible records using Prisma client based on CASL rules", | ||
@@ -13,2 +13,7 @@ "main": "dist/es6c/index.js", | ||
"require": "./dist/es6c/index.js" | ||
}, | ||
"./runtime": { | ||
"types": "./dist/types/runtime.d.ts", | ||
"import": "./dist/es6m/runtime.mjs", | ||
"require": "./dist/es6c/runtime.js" | ||
} | ||
@@ -26,4 +31,5 @@ }, | ||
"scripts": { | ||
"prebuild": "rm -rf dist/* && npm run build.types", | ||
"build": "BUILD_TYPES=es6m,es6c dx rollup -e @casl/ability/extra,@casl/ability,@prisma/client,@ucast/core,@ucast/js", | ||
"prebuild": "rm -rf dist/* && npm run build.types && npm run build.runtime", | ||
"build.runtime": "BUILD_TYPES=es6m,es6c dx rollup -i src/runtime.ts -e @casl/ability/extra,@casl/ability,@prisma/client,@ucast/core,@ucast/js", | ||
"build": "BUILD_TYPES=es6m,es6c dx rollup -e @casl/ability/extra,@casl/ability,@prisma/client,@ucast/core,@ucast/js,./runtime", | ||
"build.types": "dx tsc", | ||
@@ -52,5 +58,5 @@ "lint": "dx eslint src/ spec/", | ||
"@casl/dx": "workspace:^1.0.0", | ||
"@prisma/client": "^4.0.0", | ||
"@prisma/client": "^4.3.1", | ||
"@types/jest": "^28.0.0", | ||
"prisma": "^4.0.0" | ||
"prisma": "^4.3.1" | ||
}, | ||
@@ -60,3 +66,3 @@ "files": [ | ||
"*.d.ts", | ||
"index.js" | ||
"runtime.js" | ||
], | ||
@@ -63,0 +69,0 @@ "dependencies": { |
@@ -108,3 +108,3 @@ # CASL Prisma | ||
import { User } from '@prisma/client'; | ||
import { Model } from '@casl/prisma'; | ||
import { Model, PrismaQuery } from '@casl/prisma'; | ||
@@ -132,2 +132,60 @@ // almost the same as Prisma.UserWhereInput except that it's a higher order type | ||
## Custom PrismaClient output path | ||
Prisma allows [to generate client into a custom directory](https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/generating-prisma-client#using-a-custom-output-path) in this case `@prisma/client` doesn't re-export needed types anymore and `@casl/prisma` cannot automatically detect and infer types. In this case, we need to provide required types manually. Let's assume that we have the next configuration: | ||
```prisma | ||
generator client { | ||
provider = "prisma-client-js" | ||
output = "../src/generated/client" | ||
} | ||
``` | ||
Then we need to create a custom file for casl-prisma integration: | ||
```ts | ||
// src/casl-prisma.ts | ||
import { | ||
createAbilityFactory, | ||
createAccessibleByFactory, | ||
prismaQuery, | ||
ExtractModelName, | ||
Model | ||
} from "@casl/prisma/runtime"; | ||
import { hkt, AbilityOptions, AbilityTuple, fieldPatternMatcher, PureAbility, RawRuleFrom } from "@casl/ability"; | ||
import type { Prisma, PrismaClient } from "./generated/client"; | ||
import type { ExtractModelName, Model } from "./prisma/prismaQuery"; | ||
type ModelName = Prisma.ModelName; | ||
type ModelWhereInput = { | ||
[K in Prisma.ModelName]: Uncapitalize<K> extends keyof PrismaClient | ||
? Extract<Parameters<PrismaClient[Uncapitalize<K>]['findFirst']>[0], { where?: any }>["where"] | ||
: never | ||
}; | ||
type WhereInput<TModelName extends Prisma.ModelName> = Extract<ModelWhereInput[TModelName], Record<any, any>>; | ||
interface PrismaQueryTypeFactory extends hkt.GenericFactory { | ||
produce: WhereInput<ExtractModelName<this[0], ModelName>> | ||
} | ||
type PrismaModel = Model<Record<string, any>, string>; | ||
// Higher Order type that allows to infer passed in Prisma Model name | ||
export type PrismaQuery<T extends PrismaModel = PrismaModel> = | ||
WhereInput<ExtractModelName<T, ModelName>> & hkt.Container<PrismaQueryTypeFactory>; | ||
type WhereInputPerModel = { | ||
[K in ModelName]: WhereInput<K>; | ||
}; | ||
const createPrismaAbility = createAbilityFactory<ModelName, PrismaQuery>(); | ||
const accessibleBy = createAccessibleByFactory<WhereInputPerModel, PrismaQuery>(); | ||
export { | ||
createPrismaAbility, | ||
accessibleBy, | ||
}; | ||
``` | ||
## Want to help? | ||
@@ -134,0 +192,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 2 instances in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
74889
12.11%22
57.14%135
23.85%204
39.73%2
100%1
Infinity%