@noxify/casl-drizzle 0.1.0 → 0.2.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 +3 -3
- package/dist/index.d.mts +10 -10
- package/dist/index.mjs +1 -1
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @noxify/casl-drizzle
|
|
2
2
|
|
|
3
|
-
CASL integration for Drizzle ORM - Add type-safe authorization to your database queries
|
|
3
|
+
CASL v7 integration for Drizzle ORM ( 1.0.0-rc.3 ) - Add type-safe authorization to your database queries
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
@@ -13,11 +13,11 @@ CASL integration for Drizzle ORM - Add type-safe authorization to your database
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
npm install @noxify/casl-drizzle @casl/ability drizzle-orm@
|
|
16
|
+
npm install @noxify/casl-drizzle @casl/ability drizzle-orm@1.0.0-rc.3
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
pnpm add @noxify/casl-drizzle @casl/ability drizzle-orm@
|
|
20
|
+
pnpm add @noxify/casl-drizzle @casl/ability drizzle-orm@1.0.0-rc.3
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Setup
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbilityOptions, AbilityOptionsOf, AbilityTuple, AnyAbility, ForcedSubject,
|
|
1
|
+
import { Ability, AbilityOptions, AbilityOptionsOf, AbilityTuple, AnyAbility, ForcedSubject, RawRuleFrom, RawRuleOf, hkt } from "@casl/ability";
|
|
2
2
|
import { SQL, Table, operators } from "drizzle-orm";
|
|
3
3
|
import { DBQueryConfig, TablesRelationalConfig } from "drizzle-orm/relations";
|
|
4
4
|
import { KnownKeysOnly } from "drizzle-orm/utils";
|
|
@@ -109,7 +109,7 @@ type QueryInput<TSchema extends TablesRelationalConfig, TTableName extends keyof
|
|
|
109
109
|
*
|
|
110
110
|
* @example
|
|
111
111
|
* ```ts
|
|
112
|
-
* import {
|
|
112
|
+
* import { Ability } from "@casl/ability"
|
|
113
113
|
* import type { QueryInput, Subjects } from "@noxify/casl-drizzle"
|
|
114
114
|
*
|
|
115
115
|
* type QueryMap = {
|
|
@@ -117,7 +117,7 @@ type QueryInput<TSchema extends TablesRelationalConfig, TTableName extends keyof
|
|
|
117
117
|
* posts: QueryInput<typeof relations, "posts">
|
|
118
118
|
* }
|
|
119
119
|
*
|
|
120
|
-
* type AppAbility =
|
|
120
|
+
* type AppAbility = Ability<[string, Subjects<QueryMap>]>
|
|
121
121
|
* ```
|
|
122
122
|
*/
|
|
123
123
|
type Subjects$1<T> = { [K in keyof T]: Model<T[K], K & string> }[keyof T] | Extract<keyof T, string>;
|
|
@@ -143,7 +143,7 @@ type Subjects$1<T> = { [K in keyof T]: Model<T[K], K & string> }[keyof T] | Extr
|
|
|
143
143
|
* type AppAbility = DrizzleAbility<SubjectMap, AllowedAction>
|
|
144
144
|
* ```
|
|
145
145
|
*/
|
|
146
|
-
type DrizzleAbility<T, TActions extends string = string> =
|
|
146
|
+
type DrizzleAbility<T, TActions extends string = string> = Ability<[TActions, Subjects$1<T>], T[keyof T]>;
|
|
147
147
|
/**
|
|
148
148
|
* Helper type for defining abilities with full type inference for actions and subjects.
|
|
149
149
|
* When used with `createDrizzleAbility()`, enables IDE autocomplete for `can()` and `cannot()`
|
|
@@ -177,14 +177,14 @@ type DefineDrizzleAbility<T, TActions extends string = string> = DrizzleAbility<
|
|
|
177
177
|
/**
|
|
178
178
|
* @deprecated use accessibleBy directly instead. It will infer the types from passed Ability instance.
|
|
179
179
|
*/
|
|
180
|
-
declare const createAccessibleByFactory: <TResult extends Record<string, unknown>, TDrizzleQuery>() => <TAbility extends
|
|
180
|
+
declare const createAccessibleByFactory: <TResult extends Record<string, unknown>, TDrizzleQuery>() => <TAbility extends Ability<any, TDrizzleQuery>>(ability: TAbility, action?: TAbility["rules"][number]["action"]) => TResult;
|
|
181
181
|
declare function accessibleBy<TSubjectMap, TActions extends string = string>(ability: DrizzleAbility<TSubjectMap, TActions>, action?: TActions): Record<Extract<keyof TSubjectMap, string>, WhereInput>;
|
|
182
|
-
declare function accessibleBy<TAbility extends
|
|
182
|
+
declare function accessibleBy<TAbility extends Ability<any, any>>(ability: TAbility, action?: TAbility["rules"][number]["action"]): Record<string, WhereInput>;
|
|
183
183
|
//#endregion
|
|
184
184
|
//#region src/factories/create-ability.d.ts
|
|
185
185
|
declare function createAbilityFactory<TModelName extends string, TDrizzleQuery extends Record<string, any>>(): {
|
|
186
|
-
<T extends
|
|
187
|
-
<A extends AbilityTuple = [string, TModelName], C extends TDrizzleQuery = TDrizzleQuery>(rules?: RawRuleFrom<A, C>[], options?: AbilityOptions<A, C>):
|
|
186
|
+
<T extends Ability<any, TDrizzleQuery>>(rules?: RawRuleOf<T>[], options?: AbilityOptionsOf<T>): T;
|
|
187
|
+
<A extends AbilityTuple = [string, TModelName], C extends TDrizzleQuery = TDrizzleQuery>(rules?: RawRuleFrom<A, C>[], options?: AbilityOptions<A, C>): Ability<A, C>;
|
|
188
188
|
};
|
|
189
189
|
//#endregion
|
|
190
190
|
//#region src/query-error.d.ts
|
|
@@ -360,8 +360,8 @@ declare function none<T extends Table>(table: T, condition?: (builder: Omit<Rela
|
|
|
360
360
|
* )
|
|
361
361
|
* ```
|
|
362
362
|
*/
|
|
363
|
-
declare function createDrizzleAbilityFor(): new (...args: ConstructorParameters<typeof
|
|
364
|
-
declare function createDrizzleAbilityFor<TSubject, TActions extends string = string>(): new (...args: ConstructorParameters<typeof
|
|
363
|
+
declare function createDrizzleAbilityFor(): new (...args: ConstructorParameters<typeof Ability>) => AnyAbility;
|
|
364
|
+
declare function createDrizzleAbilityFor<TSubject, TActions extends string = string>(): new (...args: ConstructorParameters<typeof Ability>) => DrizzleAbility<TSubject, TActions>;
|
|
365
365
|
/**
|
|
366
366
|
* Create a type-safe ability with subject and action-specific conditions.
|
|
367
367
|
* Use this with a pre-defined ability type for full type inference.
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Ability as e,AbilityBuilder as t,ForbiddenError as n,fieldPatternMatcher as r}from"@casl/ability";import{CompoundCondition as i,FieldCondition as a,NULL_CONDITION as o,ObjectQueryParser as ee,buildAnd as te,createTranslatorFactory as s}from"@ucast/core";import{and as c,compare as l,createJsInterpreter as u,gt as ne,gte as re,lt as ie,lte as ae,or as d,within as oe}from"@ucast/js";import{rulesToCondition as se}from"@casl/ability/extra";import{operators as f,sql as p}from"drizzle-orm";const ce=(e,t,{get:n})=>n(t,e.field).startsWith(e.value),le=(e,t,{get:n})=>n(t,e.field).toLowerCase().startsWith(e.value.toLowerCase()),m=(e,t,{get:n})=>n(t,e.field).endsWith(e.value),ue=(e,t,{get:n})=>n(t,e.field).toLowerCase().endsWith(e.value.toLowerCase()),h=(e,t,{get:n})=>n(t,e.field).includes(e.value),g=(e,t,{get:n})=>n(t,e.field).toLowerCase().includes(e.value.toLowerCase()),_=e=>{let t=`^${e.replaceAll(/[.*+?^${}()|[\]\\]/gu,`\\$&`).replaceAll(`%`,`.*`).replaceAll(`_`,`.`)}$`;return new RegExp(t,`u`)},v=(e,t,{get:n})=>{let r=n(t,e.field);return typeof r==`string`?_(e.value).test(r):!1},y=(e,t,{get:n})=>{let r=n(t,e.field);return typeof r==`string`?_(e.value.toLowerCase()).test(r.toLowerCase()):!1},b=(e,t,{get:n})=>{let r=n(t,e.field);return(Array.isArray(r)&&r.length===0)===e.value},de=(e,t,{get:n})=>{let r=n(t,e.field);return Array.isArray(r)&&r.includes(e.value)},fe=(e,t,{get:n})=>{let r=n(t,e.field);return Array.isArray(r)&&e.value.some(e=>r.includes(e))},pe=(e,t,{get:n})=>{let r=n(t,e.field);return Array.isArray(r)&&e.value.every(e=>r.includes(e))},me=(e,t,{get:n})=>{let r=n(t,e.field);return Array.isArray(r)&&e.value.some(e=>r.includes(e))},he=(e,t,{get:n})=>{let r=n(t,e.field);return Array.isArray(r)&&e.value.every(e=>r.includes(e))},x=(e,t,{get:n})=>{let r=n(t,e.field);return Array.isArray(r)&&r.every(t=>e.value.includes(t))},S=(e,t,{get:n,interpret:r})=>{let i=n(t,e.field);return Array.isArray(i)&&i.length>0&&i.every(t=>r(e.value,t))},C=(e,t,{get:n,interpret:r})=>{let i=n(t,e.field);return Array.isArray(i)&&i.some(t=>r(e.value,t))},w=(e,t,{get:n,interpret:r})=>{let i=n(t,e.field);return typeof i==`object`&&!!i&&r(e.value,i)},T=(e,t,{interpret:n})=>e.value.every(e=>!n(e,t)),E=(e,t,{get:n})=>n(t,e.field)!==void 0===e.value,D=(e,t,{get:n})=>n(t,e.field)===null===e.value,O=(e,t,{get:n})=>n(t,e.field)!==null===e.value;function k(e){return e&&typeof e==`object`?e.valueOf():e}const A=e=>typeof e==`object`&&!!e&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null),j=(e,t)=>{if(Object.is(e,t))return!0;if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(Array.isArray(e)&&Array.isArray(t))return e.length===t.length&&e.every((e,n)=>j(e,t[n]));if(A(e)&&A(t)){let n=Object.keys(e),r=Object.keys(t);return n.length===r.length&&n.every(n=>Object.hasOwn(t,n)&&j(e[n],t[n]))}return!1},M=e=>{let t=typeof e;return e===null||t===`string`||t===`number`||t===`boolean`||t===`bigint`||e instanceof Date},N=(e,t,{get:n,compare:r})=>{let i=n(t,e.field),a=e.value;return j(i,a)?!0:M(i)&&M(a)?r(i,a)===0:!1},P=u({eq:N,equals:N,notEquals:(e,t,n)=>!N(e,t,n),in:oe,lt:ie,lte:ae,gt:ne,gte:re,startsWith:ce,istartsWith:le,endsWith:m,iendsWith:ue,contains:h,icontains:g,like:v,ilike:y,isEmpty:b,has:de,hasSome:fe,hasEvery:pe,arrayOverlaps:me,arrayContained:x,arrayContains:he,and:c,or:d,AND:c,OR:d,NOT:T,every:S,some:C,is:w,isSet:E,isNull:D,isNotNull:O,RAW:()=>!0},{get:(e,t)=>e[t],compare:(e,t)=>l(k(e),k(t))});var F=class extends Error{name=`ParsingQueryError`;static invalidArgument(e,t,n){let r=`${typeof t}(${JSON.stringify(t,null,2)})`;return new this(`"${e}" expects to receive ${n} but instead got "${r}"`)}};const I=e=>typeof e==`object`&&!!e&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null),L={type:`field`},R={type:`field`,validate:void L.validate,parse(e,t,{field:n}){return new a(`notEquals`,n,t)}},ge={type:`field`,parse:((e,t,{hasOperators:n,field:r,parse:o})=>Array.isArray(t)||!I(t)||!n(t)?new a(`notEquals`,r,t):new i(`NOT`,[o(t,{field:r})]))},z={type:`field`,validate(e,t){if(!Array.isArray(t))throw F.invalidArgument(e.name,t,`an array`)}},B={type:`field`,validate(e,t){let n=typeof t;if(!(n===`string`||n===`number`&&Number.isFinite(t)||t instanceof Date))throw F.invalidArgument(e.name,t,`comparable value`)}},V=new Set([`insensitive`,`default`]),_e={type:`field`,validate(e,t){if(!V.has(t))throw F.invalidArgument(e.name,t,`one of ${[...V].join(`, `)}`)},parse:()=>o},H={type:`field`,validate(e,t){if(typeof t!=`string`)throw F.invalidArgument(e.name,t,`string`)},parse(e,t,{query:n,field:r}){return new a(n.mode===`insensitive`?`i${e.name}`:e.name,r,t)}},U={type:`field`,validate(e,t){if(typeof t!=`string`)throw F.invalidArgument(e.name,t,`string`)},parse(e,t,{query:n,field:r}){return e.name===`ilike`||n.mode===`insensitive`?new a(`ilike`,r,t):new a(`like`,r,t)}},W={type:`compound`,validate(e,t){if(!t||typeof t!=`object`)throw F.invalidArgument(e.name,t,`an array or object`)},parse(e,t,{parse:n}){let r=(Array.isArray(t)?t:[t]).map(e=>n(e));return new i(e.name,r)}},G={type:`field`,validate(e,t){if(typeof t!=`boolean`)throw F.invalidArgument(e.name,t,`a boolean`)}},ve={type:`field`},K={type:`field`,validate(e,t){if(!Array.isArray(t))throw F.invalidArgument(e.name,t,`an array`)}},q={type:`field`,validate(e,t){if(!Array.isArray(t))throw F.invalidArgument(e.name,t,`an array`)}},J={type:`field`,parse(e,t,{field:n,parse:r}){if(!I(t))throw F.invalidArgument(e.name,t,`a query for nested relation`);return new a(e.name,n,r(t))}},Y=(e,t)=>{let n=t.parse?.bind(t);return n?{...t,parse(t,r,a){let o=n(t,r,a);if(o.operator!==t.name)throw Error(`Cannot invert "${e}" operator parser because it returns a complex Condition`);return o.operator=e,new i(`NOT`,[o])}}:{...t,parse(t,n,r){return new i(`NOT`,[new a(e,r.field,n)])}}},ye={eq:L,ne:R,not:ge,in:z,notIn:Y(`in`,z),lt:B,lte:B,gt:B,gte:B,$lt:B,$lte:B,$gt:B,$gte:B,$in:z,$nin:Y(`in`,z),mode:_e,startsWith:H,endsWith:H,contains:H,like:U,ilike:U,notLike:{type:`field`,parse:((e,t,{field:n,parse:r})=>new i(`NOT`,[r({like:t},{field:n})]))},notIlike:{type:`field`,parse:((e,t,{field:n,parse:r})=>new i(`NOT`,[r({ilike:t},{field:n})]))},isNull:G,isNotNull:G,isEmpty:G,has:ve,hasSome:K,hasEvery:K,arrayOverlaps:q,arrayContained:q,arrayContains:q,NOT:W,AND:W,OR:W,every:J,some:J,none:Y(`some`,J),is:J,isNot:Y(`is`,J),isSet:G,RAW:{type:`field`,parse(e,t){return new a(`RAW`,`RAW`,t)}}},X=s(new class extends ee{constructor(){super(ye,{defaultOperatorName:`eq`})}normalizeEqOperator(e){if(Array.isArray(e))return e.map(e=>this.normalizeEqOperator(e));if(!I(e))return e;let t=Object.keys(e);if(t.length===1&&t[0]===`eq`)return this.normalizeEqOperator(e.eq);let n={};for(let[t,r]of Object.entries(e))n[t]=this.normalizeEqOperator(r);return n}parse(e,t){let n=this.normalizeEqOperator(e);return t?.field?te(this.parseFieldOperators(t.field,n)):super.parse(n)}}().parse,P);function Z(e){if(typeof e!=`object`||!e)return e;if(Array.isArray(e))return e.map(Z);let t={};for(let[n,r]of Object.entries(e)){if(n===`OR`||n===`AND`){t[n]=Z(r);continue}if(n===`RAW`){t[n]=r;continue}if(n.startsWith(`$`)){let e=n.slice(1);t[e]=Z(r);continue}t[n]=typeof r==`object`&&r&&!Array.isArray(r)?Z(r):r}return t}const be={get(e,t){let r=se(e._ability.rulesFor(e._action,t),e=>e.inverted?{NOT:e.conditions}:e.conditions,{and:e=>({AND:e}),or:e=>({OR:e}),empty:()=>({})});if(r===null){let r=n.from(e._ability).setMessage(`It's not allowed to run "${e._action}" on "${t}"`);throw r.action=e._action,r.subjectType=t,r.subject=t,r}let i=Object.create(null);if(r.OR&&Array.isArray(r.OR)&&r.OR.length===1){let[e]=r.OR;Object.assign(i,e)}else r.OR&&(i.OR=r.OR);return Z(i)}};function xe(e,t){return new Proxy({_ability:e,_action:t},be)}function Q(){function t(t=[],n={}){return new e(t,{...n,conditionsMatcher:X,fieldMatcher:r})}return t}const $=()=>new Proxy({},{get:(e,t)=>p.raw(String(t))});function Se(e,t){if(e&&typeof e==`object`&&`_`in e&&`columns`in e._){if(!t)throw Error(`Condition is required when table is provided`);let{columns:n}=e._;return{RAW:t({...f,columns:n})}}let n=e;if(typeof n==`function`){let e=$();return{RAW:n({...f,columns:e})}}return{RAW:n}}function Ce(e,t){if(e&&typeof e==`object`&&`_`in e&&`columns`in e._){if(!t)throw Error(`Condition is required when table is provided`);let{columns:n}=e._;return{RAW:t({...f,columns:n})}}let n=e;if(typeof n==`function`){let e=$();return{RAW:n({...f,columns:e})}}return{RAW:n}}function we(e,t){if(!e)return{RAW:p`1=0`};if(e&&typeof e==`object`&&`_`in e&&`columns`in e._){if(!t)return{RAW:p`1=0`};let{columns:n}=e._;return{RAW:t({...f,columns:n})}}let n=e;if(typeof n==`function`){let e=$();return{RAW:n({...f,columns:e})}}return{RAW:n}}function Te(){return Q()}function Ee(e){let n=new t(Q());return e(n.can.bind(n),n.cannot.bind(n)),n.build()}export{F as ParsingQueryError,xe as accessibleBy,Ee as createDrizzleAbility,Te as createDrizzleAbilityFor,X as drizzleQuery,Ce as every,we as none,Se as some};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noxify/casl-drizzle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Drizzle-ORM adapter for CASL - generate Drizzle where inputs from CASL abilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ability",
|
|
@@ -38,28 +38,28 @@
|
|
|
38
38
|
"@ucast/js": "4.0.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@casl/ability": "
|
|
41
|
+
"@casl/ability": "7.0.0",
|
|
42
42
|
"@changesets/cli": "2.31.0",
|
|
43
|
-
"@electric-sql/pglite": "0.4.
|
|
43
|
+
"@electric-sql/pglite": "0.4.6",
|
|
44
44
|
"@types/js-yaml": "4.0.9",
|
|
45
45
|
"@types/node": "24.12.4",
|
|
46
|
-
"@vitest/coverage-v8": "4.1.
|
|
46
|
+
"@vitest/coverage-v8": "4.1.7",
|
|
47
47
|
"dedent": "1.7.2",
|
|
48
|
-
"drizzle-kit": "
|
|
49
|
-
"drizzle-orm": "
|
|
48
|
+
"drizzle-kit": "1.0.0-rc.3",
|
|
49
|
+
"drizzle-orm": "1.0.0-rc.3",
|
|
50
50
|
"json-schema-to-typescript": "15.0.4",
|
|
51
51
|
"node-pty": "1.1.0",
|
|
52
|
-
"oxfmt": "0.
|
|
53
|
-
"oxlint": "1.
|
|
54
|
-
"tsdown": "0.22.
|
|
55
|
-
"tsx": "4.22.
|
|
56
|
-
"typescript": "
|
|
57
|
-
"ultracite": "7.
|
|
58
|
-
"vitest": "4.1.
|
|
52
|
+
"oxfmt": "0.52.0",
|
|
53
|
+
"oxlint": "1.67.0",
|
|
54
|
+
"tsdown": "0.22.1",
|
|
55
|
+
"tsx": "4.22.3",
|
|
56
|
+
"typescript": "6.0.3",
|
|
57
|
+
"ultracite": "7.8.0",
|
|
58
|
+
"vitest": "4.1.7"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@casl/ability": "^
|
|
62
|
-
"drizzle-orm": ">=1.0.0-
|
|
61
|
+
"@casl/ability": "^7.0.0",
|
|
62
|
+
"drizzle-orm": ">=1.0.0-rc.3"
|
|
63
63
|
},
|
|
64
64
|
"engines": {
|
|
65
65
|
"node": ">=24"
|