diff --git a/.gitignore b/.gitignore index 618ab38..7223249 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ yarn-error.log* tags dist/docs.md package-lock.json +dist/ diff --git a/dist/index.d.ts b/dist/index.d.ts deleted file mode 100644 index 5676564..0000000 --- a/dist/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { VueAcl } from '../types'; -export { AclWithRouter, Behaviour, Options, PromiseChain, SetupCallback, User, UserGetter, VueRouterMeta, } from '../types'; -declare const VueAcl: VueAcl; -export default VueAcl; diff --git a/dist/index.js b/dist/index.js deleted file mode 100644 index 7061436..0000000 --- a/dist/index.js +++ /dev/null @@ -1,2 +0,0 @@ -var e,t=(e=require("browser-acl"))&&"object"==typeof e&&"default"in e?e.default:e,r={install:function(e,r,l,c){void 0===l&&(l=void 0),void 0===c&&(c={});var u="function"==typeof r?r:function(){return r},s=Boolean(c.strict),f=Object.assign({acl:{strict:s},aliases:["role"],assumeGlobal:!s,caseMode:!0,debug:!1,directive:"can",failRoute:"/",helper:!0,strict:!1},c),d=o(f),v=new t(f.acl);"function"==typeof l?l(v):l instanceof t&&(v=l),v.router=function(e){f.router=e;var r=function(e,t){var r;return t&&(r=v).can.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))||!t&&!f.strict};e.beforeEach(function(e,o,a){var n=function(e,o,a){var n=null,i=e.reduce(function(e,i){return e.then(function(e){if(!0!==e)return e;"string"==typeof i.fail&&(n=i.fail);var l,c,u=d(i),s="function"==typeof u?u(o,a,r):Promise.resolve(r.apply(void 0,[(l=u.split(" "))[0],void 0===(c=l[1])?f.assumeGlobal?t.GlobalRule:null:c]));if(f.strict&&!(s instanceof Promise))throw new Error("$route.meta.can must return a promise in strict mode");return s}).catch(function(e){return f.debug&&console.error(e),!1})},Promise.resolve(!0));return i.getFail=function(){return n},i}(e.matched.filter(function(e){return e.meta&&d(e.meta)}).map(function(e){return e.meta}),e,o);n.then(function(t){if(!0===t)return a();var r=n.getFail()||f.failRoute;"$from"===r&&(r=o.path),a("function"==typeof r?r(e,o):r)})})},f.router&&v.router(f.router);var p=function(e,r,o){var l,c,s,d,p,m,b,y=void 0!==(b=r.modifiers).disable?"disable":void 0!==b.readonly?"readonly":"hide";if(d=r.arg,Array.isArray(r.value)&&null!=(l=r.expression)&&l.startsWith("[")){var h=r.modifiers.global?n(r):a(r);s=h[0],p=h[1],m=h[2]}else if("string"==typeof r.value){var g=i(r,o,f);s=g[0],p=g[1],m=g[2]}else d&&"object"==typeof r.value?(s=d,p=r.value,m=[]):void 0===r.value&&!r.modifiers.global&&f.assumeGlobal&&(s=d,p=t.GlobalRule,m=[]);if(f.assumeGlobal&&!p&&(p=t.GlobalRule,m=m||[],s=s||d),!s||!p)throw new Error("Missing verb or verb object");var w=(c=v)[(r.modifiers.some?"some":r.modifiers.every&&"every")||"can"].apply(c,[u(),s,p].concat(m)),G=r.modifiers.not,j=e;e.hasAttribute("disabled")||(j.disabled=!1),e.hasAttribute("readOnly")||(j.readOnly=!1),(w&&G||!w&&!G)&&("hide"===y?function(e,t){var r=document.createComment(" ");Object.defineProperty(r,"setAttribute",{value:function(){}}),t.text=" ",t.elm=r,t.isComment=!0,t.tag=void 0,t.data=t.data||{},t.data.directives=void 0,t.componentInstance&&(t.componentInstance.$el=r),e.parentNode&&e.parentNode.replaceChild(r,e)}(e,o):"disable"===y?j.disabled=!0:"readonly"===y&&(j.readOnly=!0))};if([f.directive].concat(f.aliases).forEach(function(t){return e.directive(t,p)}),f.helper){var m="$"+f.directive;e.prototype[m]=function(e,t){var r;return(r=v).can.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))},e.prototype[m].not=function(e,t){var r;return!(r=v).can.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))},e.prototype[m].every=function(e,t){var r;return(r=v).every.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))},e.prototype[m].some=function(e,t){var r;return(r=v).some.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))}}}},o=function(e){return function(t){return[e.directive].concat(e.aliases||[]).map(function(e){return t[e]}).filter(Boolean).shift()}},a=function(e){var t=e.arg,r=e.value;return[t||r[0],t?r[0]:r[1],r.slice(t?1:2)]},n=function(e){var r=e.arg,o=e.value;return[r||o[0],t.GlobalRule,r?o:o.slice(1)]},i=function(e,t,r){var o=e.arg,a=e.value,n=e.modifiers,i=o?[o,a]:a.split(" "),l=i[0],c=i[1];if(c&&n.global)throw new Error("You cannot provide verb object and use global modifier at the same time");return"string"==typeof c&&r.caseMode&&c[0].match(/[a-z]/)&&"object"==typeof t.context&&(c=t.context.$data[c]),[l,c,[]]};module.exports=r; -//# sourceMappingURL=index.js.map diff --git a/dist/index.js.map b/dist/index.js.map deleted file mode 100644 index 4318b14..0000000 --- a/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import Acl from 'browser-acl'\nimport { Verb, VerbObject } from 'browser-acl'\nimport { VueConstructor, VNode, DirectiveFunction } from 'vue/types'\nimport { DirectiveBinding } from 'vue/types/options'\nimport VueRouter, { Route } from 'vue-router/types'\n\nimport {\n AclWithRouter,\n Behaviour,\n Options,\n PromiseChain,\n SetupCallback,\n User,\n UserGetter,\n VueAcl,\n VueRouterMeta,\n} from '../types'\n\nexport {\n AclWithRouter,\n Behaviour,\n Options,\n PromiseChain,\n SetupCallback,\n User,\n UserGetter,\n VueRouterMeta,\n} from '../types'\n\ninterface LooseHTMLElement extends HTMLElement {\n disabled: boolean\n readOnly: boolean\n}\n\nconst VueAcl: VueAcl = {\n install(\n Vue: VueConstructor,\n user: User | UserGetter,\n aclOrSetupCallback: Acl | SetupCallback | undefined = undefined,\n options: Partial = {},\n ): void {\n const userAccessor: Function =\n typeof user === 'function' ? user : () => user\n\n /* defaults */\n const strict = Boolean(options.strict)\n\n const opt: Options = Object.assign(\n {\n acl: { strict },\n aliases: ['role'],\n assumeGlobal: !strict,\n caseMode: true,\n debug: false,\n directive: 'can',\n failRoute: '/',\n helper: true,\n strict: false,\n },\n options,\n )\n\n const findCan = findCanWithOptions(opt)\n\n /* setup acl */\n let acl: AclWithRouter = new Acl(opt.acl) as AclWithRouter\n if (typeof aclOrSetupCallback === 'function') {\n aclOrSetupCallback(acl)\n } else if (aclOrSetupCallback instanceof Acl) {\n acl = aclOrSetupCallback\n }\n\n /* router init function */\n acl.router = function (router: VueRouter) {\n opt.router = router\n\n const canNavigate = (\n verb: string,\n verbObject: string | null,\n ...otherArgs: any[]\n ) => {\n return (\n (verbObject &&\n acl.can(userAccessor(), verb, verbObject, ...otherArgs)) ||\n (!verbObject && !opt.strict)\n )\n }\n\n /* convert 'edit Post' to ['edit', 'Post'] */\n const aclTuple = (value: string): [string, string | null] => {\n const [verb, verbObject = opt.assumeGlobal ? Acl.GlobalRule : null] =\n value.split(' ')\n return [verb, verbObject]\n }\n\n /**\n * chain all can-statements and functions as promises\n * each can-function must return a promise (in strict\n * mode at least). To break the chain return a none\n * true value\n */\n const chainCans = (\n metas: VueRouterMeta[],\n to: Route,\n from: Route,\n ): PromiseChain => {\n let fail: string | null = null\n const chain: PromiseChain = metas.reduce((chain, meta) => {\n return (\n chain\n .then((result: any) => {\n if (result !== true) {\n return result\n }\n\n if (typeof meta.fail === 'string') {\n fail = meta.fail\n }\n\n const can = findCan(meta)\n\n const nextPromise =\n typeof can === 'function'\n ? can(to, from, canNavigate)\n : Promise.resolve(canNavigate(...aclTuple(can)))\n\n if (opt.strict && !(nextPromise instanceof Promise)) {\n throw new Error(\n '$route.meta.can must return a promise in strict mode',\n )\n }\n\n return nextPromise\n })\n // convert errors to false\n .catch((error) => {\n if (opt.debug) {\n console.error(error)\n }\n return false\n })\n )\n }, Promise.resolve(true)) as PromiseChain\n chain.getFail = () => fail\n return chain\n }\n\n router.beforeEach((to: Route, from: Route, next: any) => {\n const metas = to.matched\n .filter((route) => route.meta && findCan(route.meta))\n .map((route) => route.meta)\n\n const chain = chainCans(metas, to, from)\n\n chain.then((result) => {\n if (result === true) {\n return next()\n }\n\n let fail: string | Function | null = chain.getFail() || opt.failRoute\n\n if (fail === '$from') {\n fail = from.path\n }\n\n next(typeof fail === 'function' ? fail(to, from) : fail)\n })\n })\n }\n\n /* init router */\n if (opt.router) {\n acl.router(opt.router)\n }\n\n /* directive update handler */\n const directiveHandler: DirectiveFunction = function (\n el: HTMLElement,\n binding: DirectiveBinding,\n vnode: VNode,\n ): void {\n const behaviour: Behaviour = getBehaviour(binding.modifiers)\n\n let verb, verbArg, verbObject, params\n verbArg = binding.arg\n\n if (Array.isArray(binding.value) && binding.expression?.startsWith('[')) {\n ;[verb, verbObject, params] = binding.modifiers.global\n ? arrayToGlobalExprTpl(binding)\n : arrayToExprTpl(binding)\n } else if (typeof binding.value === 'string') {\n ;[verb, verbObject, params] = stringToExprTpl(binding, vnode, opt)\n } else if (verbArg && typeof binding.value === 'object') {\n verb = verbArg\n verbObject = binding.value\n params = []\n } else if (\n binding.value === undefined &&\n !binding.modifiers.global &&\n opt.assumeGlobal\n ) {\n // Fall back to global if no value is provided\n verb = verbArg\n verbObject = Acl.GlobalRule\n params = []\n }\n\n if (opt.assumeGlobal && !verbObject) {\n verbObject = Acl.GlobalRule\n params = params || []\n verb = verb || verbArg\n }\n\n if (!verb || !verbObject) {\n throw new Error('Missing verb or verb object')\n }\n\n const aclMethod =\n (binding.modifiers.some && 'some') ||\n (binding.modifiers.every && 'every') ||\n 'can'\n\n const ok = acl[aclMethod](userAccessor(), verb, verbObject, ...params)\n const not = binding.modifiers.not\n\n const el_ = el as LooseHTMLElement\n\n if (!el.hasAttribute('disabled')) {\n el_.disabled = false\n }\n\n if (!el.hasAttribute('readOnly')) {\n el_.readOnly = false\n }\n\n if ((ok && not) || (!ok && !not)) {\n if (behaviour === 'hide') {\n commentNode(el, vnode)\n } else if (behaviour === 'disable') {\n el_.disabled = true\n } else if (behaviour === 'readonly') {\n el_.readOnly = true\n }\n }\n }\n\n /* set up directive for 'can' and aliases */\n const directiveNames = [opt.directive, ...opt.aliases]\n directiveNames.forEach((name) => Vue.directive(name, directiveHandler))\n\n /* define helpers */\n if (opt.helper) {\n const helper = `$${opt.directive}`\n /* @type AclHelper */\n Vue.prototype[helper] = function (\n verb: Verb,\n verbObject: VerbObject,\n ...args: any[]\n ) {\n return acl.can(userAccessor(), verb, verbObject, ...args)\n }\n /* @type AclHelper */\n Vue.prototype[helper].not = function (\n verb: Verb,\n verbObject: VerbObject,\n ...args: any[]\n ) {\n return !acl.can(userAccessor(), verb, verbObject, ...args)\n }\n /* @type AclHelperMany */\n Vue.prototype[helper].every = function (\n verb: Verb,\n verbObjects: VerbObject[],\n ...args: any[]\n ) {\n return acl.every(userAccessor(), verb, verbObjects, ...args)\n }\n /* @type AclHelperMany */\n Vue.prototype[helper].some = function (\n verb: Verb,\n verbObjects: VerbObject[],\n ...args: any[]\n ) {\n return acl.some(userAccessor(), verb, verbObjects, ...args)\n }\n }\n },\n}\n\nfunction getBehaviour(modifiers: any): Behaviour {\n if (typeof modifiers.disable !== 'undefined') {\n return 'disable'\n }\n if (typeof modifiers.readonly !== 'undefined') {\n return 'readonly'\n }\n return 'hide'\n}\n\n/**\n * Create comment node\n *\n * @private\n * @author https://stackoverflow.com/questions/43003976/a-custom-directive-similar-to-v-if-in-vuejs#43543814\n */\nfunction commentNode(el: HTMLElement, vnode: VNode) {\n const comment = document.createComment(' ')\n\n Object.defineProperty(comment, 'setAttribute', {\n value: () => undefined,\n })\n\n vnode.text = ' '\n vnode.elm = comment\n vnode.isComment = true\n vnode.tag = undefined\n\n vnode.data = vnode.data || {}\n vnode.data.directives = undefined\n\n if (vnode.componentInstance) {\n // @ts-ignore\n vnode.componentInstance.$el = comment\n }\n\n if (el.parentNode) {\n el.parentNode.replaceChild(comment, el)\n }\n}\n\n/**\n * Return the first property from meta that is 'can' or one of its aliases.\n */\nconst findCanWithOptions =\n (opt: Options) =>\n (meta: VueRouterMeta): string | Function => {\n return ([opt.directive, ...(opt.aliases || [])] as string[])\n .map((key: string) => meta[key])\n .filter(Boolean)\n .shift()\n }\n\n/**\n * Maps binding.value of type array to expression tuple\n */\nconst arrayToExprTpl = ({ arg, value }: DirectiveBinding) => [\n arg || value[0],\n arg ? value[0] : value[1],\n arg ? value.slice(1) : value.slice(2),\n]\n\n/**\n * Maps binding.value of type array to global expression tuple\n */\nconst arrayToGlobalExprTpl = ({ arg, value }: DirectiveBinding) => [\n arg || value[0],\n Acl.GlobalRule,\n arg ? value : value.slice(1),\n]\n\n/**\n * Maps binding.value of type string to expression tuple\n */\nconst stringToExprTpl = (\n { arg, value, modifiers }: DirectiveBinding,\n vnode: VNode,\n opt: Options,\n) => {\n let [verb, verbObject] = arg ? [arg, value] : value.split(' ')\n\n if (verbObject && modifiers.global) {\n throw new Error(\n 'You cannot provide verb object and use global modifier at the same time',\n )\n }\n\n if (\n typeof verbObject === 'string' &&\n opt.caseMode &&\n verbObject[0].match(/[a-z]/) &&\n typeof vnode.context === 'object'\n ) {\n verbObject = vnode.context.$data[verbObject]\n }\n\n return [verb, verbObject, []]\n}\n\nexport default VueAcl\n"],"names":["VueAcl","install","Vue","user","aclOrSetupCallback","options","undefined","userAccessor","strict","Boolean","opt","Object","assign","acl","aliases","assumeGlobal","caseMode","debug","directive","failRoute","helper","findCan","findCanWithOptions","Acl","router","canNavigate","verb","verbObject","can","beforeEach","to","from","next","chain","metas","fail","reduce","meta","then","result","nextPromise","Promise","resolve","split","GlobalRule","Error","catch","error","console","getFail","chainCans","matched","filter","route","map","path","directiveHandler","el","binding","vnode","verbArg","params","modifiers","behaviour","disable","readonly","arg","Array","isArray","value","expression","_binding$expression","startsWith","global","arrayToGlobalExprTpl","arrayToExprTpl","stringToExprTpl","ok","some","every","not","el_","hasAttribute","disabled","readOnly","comment","document","createComment","defineProperty","text","elm","isComment","tag","data","directives","componentInstance","$el","parentNode","replaceChild","commentNode","forEach","name","prototype","verbObjects","key","shift","slice","match","context","$data"],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.modern.js b/dist/index.modern.js deleted file mode 100644 index 0d9a641..0000000 --- a/dist/index.modern.js +++ /dev/null @@ -1,2 +0,0 @@ -import e from"browser-acl";const t={install(t,n,l,s={}){const c="function"==typeof n?n:()=>n,u=Boolean(s.strict),d=Object.assign({acl:{strict:u},aliases:["role"],assumeGlobal:!u,caseMode:!0,debug:!1,directive:"can",failRoute:"/",helper:!0,strict:!1},s),f=o(d);let m=new e(d.acl);"function"==typeof l?l(m):l instanceof e&&(m=l),m.router=function(t){d.router=t;const o=(e,t,...o)=>t&&m.can(c(),e,t,...o)||!t&&!d.strict;t.beforeEach((t,r,a)=>{const i=((t,r,a)=>{let i=null;const n=t.reduce((t,n)=>t.then(t=>{if(!0!==t)return t;"string"==typeof n.fail&&(i=n.fail);const l=f(n),s="function"==typeof l?l(r,a,o):Promise.resolve(o(...(t=>{const[o,r=(d.assumeGlobal?e.GlobalRule:null)]=t.split(" ");return[o,r]})(l)));if(d.strict&&!(s instanceof Promise))throw new Error("$route.meta.can must return a promise in strict mode");return s}).catch(e=>(d.debug&&console.error(e),!1)),Promise.resolve(!0));return n.getFail=()=>i,n})(t.matched.filter(e=>e.meta&&f(e.meta)).map(e=>e.meta),t,r);i.then(e=>{if(!0===e)return a();let o=i.getFail()||d.failRoute;"$from"===o&&(o=r.path),a("function"==typeof o?o(t,r):o)})})},d.router&&m.router(d.router);const p=function(t,o,n){var l;const s=void 0!==(u=o.modifiers).disable?"disable":void 0!==u.readonly?"readonly":"hide";var u;let f,p,b,v;if(p=o.arg,Array.isArray(o.value)&&null!=(l=o.expression)&&l.startsWith("[")?[f,b,v]=o.modifiers.global?a(o):r(o):"string"==typeof o.value?[f,b,v]=i(o,n,d):p&&"object"==typeof o.value?(f=p,b=o.value,v=[]):void 0===o.value&&!o.modifiers.global&&d.assumeGlobal&&(f=p,b=e.GlobalRule,v=[]),d.assumeGlobal&&!b&&(b=e.GlobalRule,v=v||[],f=f||p),!f||!b)throw new Error("Missing verb or verb object");const y=m[(o.modifiers.some?"some":o.modifiers.every&&"every")||"can"](c(),f,b,...v),h=o.modifiers.not,g=t;t.hasAttribute("disabled")||(g.disabled=!1),t.hasAttribute("readOnly")||(g.readOnly=!1),(y&&h||!y&&!h)&&("hide"===s?function(e,t){const o=document.createComment(" ");Object.defineProperty(o,"setAttribute",{value:()=>{}}),t.text=" ",t.elm=o,t.isComment=!0,t.tag=void 0,t.data=t.data||{},t.data.directives=void 0,t.componentInstance&&(t.componentInstance.$el=o),e.parentNode&&e.parentNode.replaceChild(o,e)}(t,n):"disable"===s?g.disabled=!0:"readonly"===s&&(g.readOnly=!0))};if([d.directive,...d.aliases].forEach(e=>t.directive(e,p)),d.helper){const e="$"+d.directive;t.prototype[e]=function(e,t,...o){return m.can(c(),e,t,...o)},t.prototype[e].not=function(e,t,...o){return!m.can(c(),e,t,...o)},t.prototype[e].every=function(e,t,...o){return m.every(c(),e,t,...o)},t.prototype[e].some=function(e,t,...o){return m.some(c(),e,t,...o)}}}},o=e=>t=>[e.directive,...e.aliases||[]].map(e=>t[e]).filter(Boolean).shift(),r=({arg:e,value:t})=>[e||t[0],e?t[0]:t[1],t.slice(e?1:2)],a=({arg:t,value:o})=>[t||o[0],e.GlobalRule,t?o:o.slice(1)],i=({arg:e,value:t,modifiers:o},r,a)=>{let[i,n]=e?[e,t]:t.split(" ");if(n&&o.global)throw new Error("You cannot provide verb object and use global modifier at the same time");return"string"==typeof n&&a.caseMode&&n[0].match(/[a-z]/)&&"object"==typeof r.context&&(n=r.context.$data[n]),[i,n,[]]};export default t; -//# sourceMappingURL=index.modern.js.map diff --git a/dist/index.modern.js.map b/dist/index.modern.js.map deleted file mode 100644 index 7d9ad17..0000000 --- a/dist/index.modern.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.modern.js","sources":["../src/index.ts"],"sourcesContent":["import Acl from 'browser-acl'\nimport { Verb, VerbObject } from 'browser-acl'\nimport { VueConstructor, VNode, DirectiveFunction } from 'vue/types'\nimport { DirectiveBinding } from 'vue/types/options'\nimport VueRouter, { Route } from 'vue-router/types'\n\nimport {\n AclWithRouter,\n Behaviour,\n Options,\n PromiseChain,\n SetupCallback,\n User,\n UserGetter,\n VueAcl,\n VueRouterMeta,\n} from '../types'\n\nexport {\n AclWithRouter,\n Behaviour,\n Options,\n PromiseChain,\n SetupCallback,\n User,\n UserGetter,\n VueRouterMeta,\n} from '../types'\n\ninterface LooseHTMLElement extends HTMLElement {\n disabled: boolean\n readOnly: boolean\n}\n\nconst VueAcl: VueAcl = {\n install(\n Vue: VueConstructor,\n user: User | UserGetter,\n aclOrSetupCallback: Acl | SetupCallback | undefined = undefined,\n options: Partial = {},\n ): void {\n const userAccessor: Function =\n typeof user === 'function' ? user : () => user\n\n /* defaults */\n const strict = Boolean(options.strict)\n\n const opt: Options = Object.assign(\n {\n acl: { strict },\n aliases: ['role'],\n assumeGlobal: !strict,\n caseMode: true,\n debug: false,\n directive: 'can',\n failRoute: '/',\n helper: true,\n strict: false,\n },\n options,\n )\n\n const findCan = findCanWithOptions(opt)\n\n /* setup acl */\n let acl: AclWithRouter = new Acl(opt.acl) as AclWithRouter\n if (typeof aclOrSetupCallback === 'function') {\n aclOrSetupCallback(acl)\n } else if (aclOrSetupCallback instanceof Acl) {\n acl = aclOrSetupCallback\n }\n\n /* router init function */\n acl.router = function (router: VueRouter) {\n opt.router = router\n\n const canNavigate = (\n verb: string,\n verbObject: string | null,\n ...otherArgs: any[]\n ) => {\n return (\n (verbObject &&\n acl.can(userAccessor(), verb, verbObject, ...otherArgs)) ||\n (!verbObject && !opt.strict)\n )\n }\n\n /* convert 'edit Post' to ['edit', 'Post'] */\n const aclTuple = (value: string): [string, string | null] => {\n const [verb, verbObject = opt.assumeGlobal ? Acl.GlobalRule : null] =\n value.split(' ')\n return [verb, verbObject]\n }\n\n /**\n * chain all can-statements and functions as promises\n * each can-function must return a promise (in strict\n * mode at least). To break the chain return a none\n * true value\n */\n const chainCans = (\n metas: VueRouterMeta[],\n to: Route,\n from: Route,\n ): PromiseChain => {\n let fail: string | null = null\n const chain: PromiseChain = metas.reduce((chain, meta) => {\n return (\n chain\n .then((result: any) => {\n if (result !== true) {\n return result\n }\n\n if (typeof meta.fail === 'string') {\n fail = meta.fail\n }\n\n const can = findCan(meta)\n\n const nextPromise =\n typeof can === 'function'\n ? can(to, from, canNavigate)\n : Promise.resolve(canNavigate(...aclTuple(can)))\n\n if (opt.strict && !(nextPromise instanceof Promise)) {\n throw new Error(\n '$route.meta.can must return a promise in strict mode',\n )\n }\n\n return nextPromise\n })\n // convert errors to false\n .catch((error) => {\n if (opt.debug) {\n console.error(error)\n }\n return false\n })\n )\n }, Promise.resolve(true)) as PromiseChain\n chain.getFail = () => fail\n return chain\n }\n\n router.beforeEach((to: Route, from: Route, next: any) => {\n const metas = to.matched\n .filter((route) => route.meta && findCan(route.meta))\n .map((route) => route.meta)\n\n const chain = chainCans(metas, to, from)\n\n chain.then((result) => {\n if (result === true) {\n return next()\n }\n\n let fail: string | Function | null = chain.getFail() || opt.failRoute\n\n if (fail === '$from') {\n fail = from.path\n }\n\n next(typeof fail === 'function' ? fail(to, from) : fail)\n })\n })\n }\n\n /* init router */\n if (opt.router) {\n acl.router(opt.router)\n }\n\n /* directive update handler */\n const directiveHandler: DirectiveFunction = function (\n el: HTMLElement,\n binding: DirectiveBinding,\n vnode: VNode,\n ): void {\n const behaviour: Behaviour = getBehaviour(binding.modifiers)\n\n let verb, verbArg, verbObject, params\n verbArg = binding.arg\n\n if (Array.isArray(binding.value) && binding.expression?.startsWith('[')) {\n ;[verb, verbObject, params] = binding.modifiers.global\n ? arrayToGlobalExprTpl(binding)\n : arrayToExprTpl(binding)\n } else if (typeof binding.value === 'string') {\n ;[verb, verbObject, params] = stringToExprTpl(binding, vnode, opt)\n } else if (verbArg && typeof binding.value === 'object') {\n verb = verbArg\n verbObject = binding.value\n params = []\n } else if (\n binding.value === undefined &&\n !binding.modifiers.global &&\n opt.assumeGlobal\n ) {\n // Fall back to global if no value is provided\n verb = verbArg\n verbObject = Acl.GlobalRule\n params = []\n }\n\n if (opt.assumeGlobal && !verbObject) {\n verbObject = Acl.GlobalRule\n params = params || []\n verb = verb || verbArg\n }\n\n if (!verb || !verbObject) {\n throw new Error('Missing verb or verb object')\n }\n\n const aclMethod =\n (binding.modifiers.some && 'some') ||\n (binding.modifiers.every && 'every') ||\n 'can'\n\n const ok = acl[aclMethod](userAccessor(), verb, verbObject, ...params)\n const not = binding.modifiers.not\n\n const el_ = el as LooseHTMLElement\n\n if (!el.hasAttribute('disabled')) {\n el_.disabled = false\n }\n\n if (!el.hasAttribute('readOnly')) {\n el_.readOnly = false\n }\n\n if ((ok && not) || (!ok && !not)) {\n if (behaviour === 'hide') {\n commentNode(el, vnode)\n } else if (behaviour === 'disable') {\n el_.disabled = true\n } else if (behaviour === 'readonly') {\n el_.readOnly = true\n }\n }\n }\n\n /* set up directive for 'can' and aliases */\n const directiveNames = [opt.directive, ...opt.aliases]\n directiveNames.forEach((name) => Vue.directive(name, directiveHandler))\n\n /* define helpers */\n if (opt.helper) {\n const helper = `$${opt.directive}`\n /* @type AclHelper */\n Vue.prototype[helper] = function (\n verb: Verb,\n verbObject: VerbObject,\n ...args: any[]\n ) {\n return acl.can(userAccessor(), verb, verbObject, ...args)\n }\n /* @type AclHelper */\n Vue.prototype[helper].not = function (\n verb: Verb,\n verbObject: VerbObject,\n ...args: any[]\n ) {\n return !acl.can(userAccessor(), verb, verbObject, ...args)\n }\n /* @type AclHelperMany */\n Vue.prototype[helper].every = function (\n verb: Verb,\n verbObjects: VerbObject[],\n ...args: any[]\n ) {\n return acl.every(userAccessor(), verb, verbObjects, ...args)\n }\n /* @type AclHelperMany */\n Vue.prototype[helper].some = function (\n verb: Verb,\n verbObjects: VerbObject[],\n ...args: any[]\n ) {\n return acl.some(userAccessor(), verb, verbObjects, ...args)\n }\n }\n },\n}\n\nfunction getBehaviour(modifiers: any): Behaviour {\n if (typeof modifiers.disable !== 'undefined') {\n return 'disable'\n }\n if (typeof modifiers.readonly !== 'undefined') {\n return 'readonly'\n }\n return 'hide'\n}\n\n/**\n * Create comment node\n *\n * @private\n * @author https://stackoverflow.com/questions/43003976/a-custom-directive-similar-to-v-if-in-vuejs#43543814\n */\nfunction commentNode(el: HTMLElement, vnode: VNode) {\n const comment = document.createComment(' ')\n\n Object.defineProperty(comment, 'setAttribute', {\n value: () => undefined,\n })\n\n vnode.text = ' '\n vnode.elm = comment\n vnode.isComment = true\n vnode.tag = undefined\n\n vnode.data = vnode.data || {}\n vnode.data.directives = undefined\n\n if (vnode.componentInstance) {\n // @ts-ignore\n vnode.componentInstance.$el = comment\n }\n\n if (el.parentNode) {\n el.parentNode.replaceChild(comment, el)\n }\n}\n\n/**\n * Return the first property from meta that is 'can' or one of its aliases.\n */\nconst findCanWithOptions =\n (opt: Options) =>\n (meta: VueRouterMeta): string | Function => {\n return ([opt.directive, ...(opt.aliases || [])] as string[])\n .map((key: string) => meta[key])\n .filter(Boolean)\n .shift()\n }\n\n/**\n * Maps binding.value of type array to expression tuple\n */\nconst arrayToExprTpl = ({ arg, value }: DirectiveBinding) => [\n arg || value[0],\n arg ? value[0] : value[1],\n arg ? value.slice(1) : value.slice(2),\n]\n\n/**\n * Maps binding.value of type array to global expression tuple\n */\nconst arrayToGlobalExprTpl = ({ arg, value }: DirectiveBinding) => [\n arg || value[0],\n Acl.GlobalRule,\n arg ? value : value.slice(1),\n]\n\n/**\n * Maps binding.value of type string to expression tuple\n */\nconst stringToExprTpl = (\n { arg, value, modifiers }: DirectiveBinding,\n vnode: VNode,\n opt: Options,\n) => {\n let [verb, verbObject] = arg ? [arg, value] : value.split(' ')\n\n if (verbObject && modifiers.global) {\n throw new Error(\n 'You cannot provide verb object and use global modifier at the same time',\n )\n }\n\n if (\n typeof verbObject === 'string' &&\n opt.caseMode &&\n verbObject[0].match(/[a-z]/) &&\n typeof vnode.context === 'object'\n ) {\n verbObject = vnode.context.$data[verbObject]\n }\n\n return [verb, verbObject, []]\n}\n\nexport default VueAcl\n"],"names":["VueAcl","install","Vue","user","aclOrSetupCallback","options","userAccessor","strict","Boolean","opt","Object","assign","acl","aliases","assumeGlobal","caseMode","debug","directive","failRoute","helper","findCan","findCanWithOptions","Acl","router","canNavigate","verb","verbObject","otherArgs","can","beforeEach","to","from","next","chain","metas","fail","reduce","meta","then","result","nextPromise","Promise","resolve","value","GlobalRule","split","aclTuple","Error","catch","error","console","getFail","chainCans","matched","filter","route","map","path","directiveHandler","el","binding","vnode","behaviour","modifiers","disable","readonly","verbArg","params","arg","Array","isArray","expression","_binding$expression","startsWith","global","arrayToGlobalExprTpl","arrayToExprTpl","stringToExprTpl","undefined","ok","some","every","not","el_","hasAttribute","disabled","readOnly","comment","document","createComment","defineProperty","text","elm","isComment","tag","data","directives","componentInstance","$el","parentNode","replaceChild","commentNode","forEach","name","prototype","args","verbObjects","key","shift","slice","match","context","$data"],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.module.js b/dist/index.module.js deleted file mode 100644 index c5d9c4f..0000000 --- a/dist/index.module.js +++ /dev/null @@ -1,2 +0,0 @@ -import e from"browser-acl";var t={install:function(t,i,l,c){void 0===l&&(l=void 0),void 0===c&&(c={});var u="function"==typeof i?i:function(){return i},s=Boolean(c.strict),f=Object.assign({acl:{strict:s},aliases:["role"],assumeGlobal:!s,caseMode:!0,debug:!1,directive:"can",failRoute:"/",helper:!0,strict:!1},c),d=r(f),v=new e(f.acl);"function"==typeof l?l(v):l instanceof e&&(v=l),v.router=function(t){f.router=t;var r=function(e,t){var r;return t&&(r=v).can.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))||!t&&!f.strict};t.beforeEach(function(t,o,a){var n=function(t,o,a){var n=null,i=t.reduce(function(t,i){return t.then(function(t){if(!0!==t)return t;"string"==typeof i.fail&&(n=i.fail);var l,c,u=d(i),s="function"==typeof u?u(o,a,r):Promise.resolve(r.apply(void 0,[(l=u.split(" "))[0],void 0===(c=l[1])?f.assumeGlobal?e.GlobalRule:null:c]));if(f.strict&&!(s instanceof Promise))throw new Error("$route.meta.can must return a promise in strict mode");return s}).catch(function(e){return f.debug&&console.error(e),!1})},Promise.resolve(!0));return i.getFail=function(){return n},i}(t.matched.filter(function(e){return e.meta&&d(e.meta)}).map(function(e){return e.meta}),t,o);n.then(function(e){if(!0===e)return a();var r=n.getFail()||f.failRoute;"$from"===r&&(r=o.path),a("function"==typeof r?r(t,o):r)})})},f.router&&v.router(f.router);var p=function(t,r,i){var l,c,s,d,p,m,b,y=void 0!==(b=r.modifiers).disable?"disable":void 0!==b.readonly?"readonly":"hide";if(d=r.arg,Array.isArray(r.value)&&null!=(l=r.expression)&&l.startsWith("[")){var h=r.modifiers.global?a(r):o(r);s=h[0],p=h[1],m=h[2]}else if("string"==typeof r.value){var g=n(r,i,f);s=g[0],p=g[1],m=g[2]}else d&&"object"==typeof r.value?(s=d,p=r.value,m=[]):void 0===r.value&&!r.modifiers.global&&f.assumeGlobal&&(s=d,p=e.GlobalRule,m=[]);if(f.assumeGlobal&&!p&&(p=e.GlobalRule,m=m||[],s=s||d),!s||!p)throw new Error("Missing verb or verb object");var w=(c=v)[(r.modifiers.some?"some":r.modifiers.every&&"every")||"can"].apply(c,[u(),s,p].concat(m)),G=r.modifiers.not,j=t;t.hasAttribute("disabled")||(j.disabled=!1),t.hasAttribute("readOnly")||(j.readOnly=!1),(w&&G||!w&&!G)&&("hide"===y?function(e,t){var r=document.createComment(" ");Object.defineProperty(r,"setAttribute",{value:function(){}}),t.text=" ",t.elm=r,t.isComment=!0,t.tag=void 0,t.data=t.data||{},t.data.directives=void 0,t.componentInstance&&(t.componentInstance.$el=r),e.parentNode&&e.parentNode.replaceChild(r,e)}(t,i):"disable"===y?j.disabled=!0:"readonly"===y&&(j.readOnly=!0))};if([f.directive].concat(f.aliases).forEach(function(e){return t.directive(e,p)}),f.helper){var m="$"+f.directive;t.prototype[m]=function(e,t){var r;return(r=v).can.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))},t.prototype[m].not=function(e,t){var r;return!(r=v).can.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))},t.prototype[m].every=function(e,t){var r;return(r=v).every.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))},t.prototype[m].some=function(e,t){var r;return(r=v).some.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))}}}},r=function(e){return function(t){return[e.directive].concat(e.aliases||[]).map(function(e){return t[e]}).filter(Boolean).shift()}},o=function(e){var t=e.arg,r=e.value;return[t||r[0],t?r[0]:r[1],r.slice(t?1:2)]},a=function(t){var r=t.arg,o=t.value;return[r||o[0],e.GlobalRule,r?o:o.slice(1)]},n=function(e,t,r){var o=e.arg,a=e.value,n=e.modifiers,i=o?[o,a]:a.split(" "),l=i[0],c=i[1];if(c&&n.global)throw new Error("You cannot provide verb object and use global modifier at the same time");return"string"==typeof c&&r.caseMode&&c[0].match(/[a-z]/)&&"object"==typeof t.context&&(c=t.context.$data[c]),[l,c,[]]};export default t; -//# sourceMappingURL=index.module.js.map diff --git a/dist/index.module.js.map b/dist/index.module.js.map deleted file mode 100644 index 302a09b..0000000 --- a/dist/index.module.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.module.js","sources":["../src/index.ts"],"sourcesContent":["import Acl from 'browser-acl'\nimport { Verb, VerbObject } from 'browser-acl'\nimport { VueConstructor, VNode, DirectiveFunction } from 'vue/types'\nimport { DirectiveBinding } from 'vue/types/options'\nimport VueRouter, { Route } from 'vue-router/types'\n\nimport {\n AclWithRouter,\n Behaviour,\n Options,\n PromiseChain,\n SetupCallback,\n User,\n UserGetter,\n VueAcl,\n VueRouterMeta,\n} from '../types'\n\nexport {\n AclWithRouter,\n Behaviour,\n Options,\n PromiseChain,\n SetupCallback,\n User,\n UserGetter,\n VueRouterMeta,\n} from '../types'\n\ninterface LooseHTMLElement extends HTMLElement {\n disabled: boolean\n readOnly: boolean\n}\n\nconst VueAcl: VueAcl = {\n install(\n Vue: VueConstructor,\n user: User | UserGetter,\n aclOrSetupCallback: Acl | SetupCallback | undefined = undefined,\n options: Partial = {},\n ): void {\n const userAccessor: Function =\n typeof user === 'function' ? user : () => user\n\n /* defaults */\n const strict = Boolean(options.strict)\n\n const opt: Options = Object.assign(\n {\n acl: { strict },\n aliases: ['role'],\n assumeGlobal: !strict,\n caseMode: true,\n debug: false,\n directive: 'can',\n failRoute: '/',\n helper: true,\n strict: false,\n },\n options,\n )\n\n const findCan = findCanWithOptions(opt)\n\n /* setup acl */\n let acl: AclWithRouter = new Acl(opt.acl) as AclWithRouter\n if (typeof aclOrSetupCallback === 'function') {\n aclOrSetupCallback(acl)\n } else if (aclOrSetupCallback instanceof Acl) {\n acl = aclOrSetupCallback\n }\n\n /* router init function */\n acl.router = function (router: VueRouter) {\n opt.router = router\n\n const canNavigate = (\n verb: string,\n verbObject: string | null,\n ...otherArgs: any[]\n ) => {\n return (\n (verbObject &&\n acl.can(userAccessor(), verb, verbObject, ...otherArgs)) ||\n (!verbObject && !opt.strict)\n )\n }\n\n /* convert 'edit Post' to ['edit', 'Post'] */\n const aclTuple = (value: string): [string, string | null] => {\n const [verb, verbObject = opt.assumeGlobal ? Acl.GlobalRule : null] =\n value.split(' ')\n return [verb, verbObject]\n }\n\n /**\n * chain all can-statements and functions as promises\n * each can-function must return a promise (in strict\n * mode at least). To break the chain return a none\n * true value\n */\n const chainCans = (\n metas: VueRouterMeta[],\n to: Route,\n from: Route,\n ): PromiseChain => {\n let fail: string | null = null\n const chain: PromiseChain = metas.reduce((chain, meta) => {\n return (\n chain\n .then((result: any) => {\n if (result !== true) {\n return result\n }\n\n if (typeof meta.fail === 'string') {\n fail = meta.fail\n }\n\n const can = findCan(meta)\n\n const nextPromise =\n typeof can === 'function'\n ? can(to, from, canNavigate)\n : Promise.resolve(canNavigate(...aclTuple(can)))\n\n if (opt.strict && !(nextPromise instanceof Promise)) {\n throw new Error(\n '$route.meta.can must return a promise in strict mode',\n )\n }\n\n return nextPromise\n })\n // convert errors to false\n .catch((error) => {\n if (opt.debug) {\n console.error(error)\n }\n return false\n })\n )\n }, Promise.resolve(true)) as PromiseChain\n chain.getFail = () => fail\n return chain\n }\n\n router.beforeEach((to: Route, from: Route, next: any) => {\n const metas = to.matched\n .filter((route) => route.meta && findCan(route.meta))\n .map((route) => route.meta)\n\n const chain = chainCans(metas, to, from)\n\n chain.then((result) => {\n if (result === true) {\n return next()\n }\n\n let fail: string | Function | null = chain.getFail() || opt.failRoute\n\n if (fail === '$from') {\n fail = from.path\n }\n\n next(typeof fail === 'function' ? fail(to, from) : fail)\n })\n })\n }\n\n /* init router */\n if (opt.router) {\n acl.router(opt.router)\n }\n\n /* directive update handler */\n const directiveHandler: DirectiveFunction = function (\n el: HTMLElement,\n binding: DirectiveBinding,\n vnode: VNode,\n ): void {\n const behaviour: Behaviour = getBehaviour(binding.modifiers)\n\n let verb, verbArg, verbObject, params\n verbArg = binding.arg\n\n if (Array.isArray(binding.value) && binding.expression?.startsWith('[')) {\n ;[verb, verbObject, params] = binding.modifiers.global\n ? arrayToGlobalExprTpl(binding)\n : arrayToExprTpl(binding)\n } else if (typeof binding.value === 'string') {\n ;[verb, verbObject, params] = stringToExprTpl(binding, vnode, opt)\n } else if (verbArg && typeof binding.value === 'object') {\n verb = verbArg\n verbObject = binding.value\n params = []\n } else if (\n binding.value === undefined &&\n !binding.modifiers.global &&\n opt.assumeGlobal\n ) {\n // Fall back to global if no value is provided\n verb = verbArg\n verbObject = Acl.GlobalRule\n params = []\n }\n\n if (opt.assumeGlobal && !verbObject) {\n verbObject = Acl.GlobalRule\n params = params || []\n verb = verb || verbArg\n }\n\n if (!verb || !verbObject) {\n throw new Error('Missing verb or verb object')\n }\n\n const aclMethod =\n (binding.modifiers.some && 'some') ||\n (binding.modifiers.every && 'every') ||\n 'can'\n\n const ok = acl[aclMethod](userAccessor(), verb, verbObject, ...params)\n const not = binding.modifiers.not\n\n const el_ = el as LooseHTMLElement\n\n if (!el.hasAttribute('disabled')) {\n el_.disabled = false\n }\n\n if (!el.hasAttribute('readOnly')) {\n el_.readOnly = false\n }\n\n if ((ok && not) || (!ok && !not)) {\n if (behaviour === 'hide') {\n commentNode(el, vnode)\n } else if (behaviour === 'disable') {\n el_.disabled = true\n } else if (behaviour === 'readonly') {\n el_.readOnly = true\n }\n }\n }\n\n /* set up directive for 'can' and aliases */\n const directiveNames = [opt.directive, ...opt.aliases]\n directiveNames.forEach((name) => Vue.directive(name, directiveHandler))\n\n /* define helpers */\n if (opt.helper) {\n const helper = `$${opt.directive}`\n /* @type AclHelper */\n Vue.prototype[helper] = function (\n verb: Verb,\n verbObject: VerbObject,\n ...args: any[]\n ) {\n return acl.can(userAccessor(), verb, verbObject, ...args)\n }\n /* @type AclHelper */\n Vue.prototype[helper].not = function (\n verb: Verb,\n verbObject: VerbObject,\n ...args: any[]\n ) {\n return !acl.can(userAccessor(), verb, verbObject, ...args)\n }\n /* @type AclHelperMany */\n Vue.prototype[helper].every = function (\n verb: Verb,\n verbObjects: VerbObject[],\n ...args: any[]\n ) {\n return acl.every(userAccessor(), verb, verbObjects, ...args)\n }\n /* @type AclHelperMany */\n Vue.prototype[helper].some = function (\n verb: Verb,\n verbObjects: VerbObject[],\n ...args: any[]\n ) {\n return acl.some(userAccessor(), verb, verbObjects, ...args)\n }\n }\n },\n}\n\nfunction getBehaviour(modifiers: any): Behaviour {\n if (typeof modifiers.disable !== 'undefined') {\n return 'disable'\n }\n if (typeof modifiers.readonly !== 'undefined') {\n return 'readonly'\n }\n return 'hide'\n}\n\n/**\n * Create comment node\n *\n * @private\n * @author https://stackoverflow.com/questions/43003976/a-custom-directive-similar-to-v-if-in-vuejs#43543814\n */\nfunction commentNode(el: HTMLElement, vnode: VNode) {\n const comment = document.createComment(' ')\n\n Object.defineProperty(comment, 'setAttribute', {\n value: () => undefined,\n })\n\n vnode.text = ' '\n vnode.elm = comment\n vnode.isComment = true\n vnode.tag = undefined\n\n vnode.data = vnode.data || {}\n vnode.data.directives = undefined\n\n if (vnode.componentInstance) {\n // @ts-ignore\n vnode.componentInstance.$el = comment\n }\n\n if (el.parentNode) {\n el.parentNode.replaceChild(comment, el)\n }\n}\n\n/**\n * Return the first property from meta that is 'can' or one of its aliases.\n */\nconst findCanWithOptions =\n (opt: Options) =>\n (meta: VueRouterMeta): string | Function => {\n return ([opt.directive, ...(opt.aliases || [])] as string[])\n .map((key: string) => meta[key])\n .filter(Boolean)\n .shift()\n }\n\n/**\n * Maps binding.value of type array to expression tuple\n */\nconst arrayToExprTpl = ({ arg, value }: DirectiveBinding) => [\n arg || value[0],\n arg ? value[0] : value[1],\n arg ? value.slice(1) : value.slice(2),\n]\n\n/**\n * Maps binding.value of type array to global expression tuple\n */\nconst arrayToGlobalExprTpl = ({ arg, value }: DirectiveBinding) => [\n arg || value[0],\n Acl.GlobalRule,\n arg ? value : value.slice(1),\n]\n\n/**\n * Maps binding.value of type string to expression tuple\n */\nconst stringToExprTpl = (\n { arg, value, modifiers }: DirectiveBinding,\n vnode: VNode,\n opt: Options,\n) => {\n let [verb, verbObject] = arg ? [arg, value] : value.split(' ')\n\n if (verbObject && modifiers.global) {\n throw new Error(\n 'You cannot provide verb object and use global modifier at the same time',\n )\n }\n\n if (\n typeof verbObject === 'string' &&\n opt.caseMode &&\n verbObject[0].match(/[a-z]/) &&\n typeof vnode.context === 'object'\n ) {\n verbObject = vnode.context.$data[verbObject]\n }\n\n return [verb, verbObject, []]\n}\n\nexport default VueAcl\n"],"names":["VueAcl","install","Vue","user","aclOrSetupCallback","options","undefined","userAccessor","strict","Boolean","opt","Object","assign","acl","aliases","assumeGlobal","caseMode","debug","directive","failRoute","helper","findCan","findCanWithOptions","Acl","router","canNavigate","verb","verbObject","can","beforeEach","to","from","next","chain","metas","fail","reduce","meta","then","result","nextPromise","Promise","resolve","split","GlobalRule","Error","catch","error","console","getFail","chainCans","matched","filter","route","map","path","directiveHandler","el","binding","vnode","verbArg","params","modifiers","behaviour","disable","readonly","arg","Array","isArray","value","expression","_binding$expression","startsWith","global","arrayToGlobalExprTpl","arrayToExprTpl","stringToExprTpl","ok","some","every","not","el_","hasAttribute","disabled","readOnly","comment","document","createComment","defineProperty","text","elm","isComment","tag","data","directives","componentInstance","$el","parentNode","replaceChild","commentNode","forEach","name","prototype","verbObjects","key","shift","slice","match","context","$data"],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.umd.js b/dist/index.umd.js deleted file mode 100644 index ac90345..0000000 --- a/dist/index.umd.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("browser-acl")):"function"==typeof define&&define.amd?define(["browser-acl"],t):(e=e||self).VueBrowserAcl=t(e.browserAcl)}(this,function(e){e=e&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e;var t={install:function(t,i,l,c){void 0===l&&(l=void 0),void 0===c&&(c={});var u="function"==typeof i?i:function(){return i},s=Boolean(c.strict),f=Object.assign({acl:{strict:s},aliases:["role"],assumeGlobal:!s,caseMode:!0,debug:!1,directive:"can",failRoute:"/",helper:!0,strict:!1},c),d=r(f),v=new e(f.acl);"function"==typeof l?l(v):l instanceof e&&(v=l),v.router=function(t){f.router=t;var r=function(e,t){var r;return t&&(r=v).can.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))||!t&&!f.strict};t.beforeEach(function(t,o,n){var a=function(t,o,n){var a=null,i=t.reduce(function(t,i){return t.then(function(t){if(!0!==t)return t;"string"==typeof i.fail&&(a=i.fail);var l,c,u=d(i),s="function"==typeof u?u(o,n,r):Promise.resolve(r.apply(void 0,[(l=u.split(" "))[0],void 0===(c=l[1])?f.assumeGlobal?e.GlobalRule:null:c]));if(f.strict&&!(s instanceof Promise))throw new Error("$route.meta.can must return a promise in strict mode");return s}).catch(function(e){return f.debug&&console.error(e),!1})},Promise.resolve(!0));return i.getFail=function(){return a},i}(t.matched.filter(function(e){return e.meta&&d(e.meta)}).map(function(e){return e.meta}),t,o);a.then(function(e){if(!0===e)return n();var r=a.getFail()||f.failRoute;"$from"===r&&(r=o.path),n("function"==typeof r?r(t,o):r)})})},f.router&&v.router(f.router);var p=function(t,r,i){var l,c,s,d,p,m,b,y=void 0!==(b=r.modifiers).disable?"disable":void 0!==b.readonly?"readonly":"hide";if(d=r.arg,Array.isArray(r.value)&&null!=(l=r.expression)&&l.startsWith("[")){var h=r.modifiers.global?n(r):o(r);s=h[0],p=h[1],m=h[2]}else if("string"==typeof r.value){var g=a(r,i,f);s=g[0],p=g[1],m=g[2]}else d&&"object"==typeof r.value?(s=d,p=r.value,m=[]):void 0===r.value&&!r.modifiers.global&&f.assumeGlobal&&(s=d,p=e.GlobalRule,m=[]);if(f.assumeGlobal&&!p&&(p=e.GlobalRule,m=m||[],s=s||d),!s||!p)throw new Error("Missing verb or verb object");var w=(c=v)[(r.modifiers.some?"some":r.modifiers.every&&"every")||"can"].apply(c,[u(),s,p].concat(m)),j=r.modifiers.not,G=t;t.hasAttribute("disabled")||(G.disabled=!1),t.hasAttribute("readOnly")||(G.readOnly=!1),(w&&j||!w&&!j)&&("hide"===y?function(e,t){var r=document.createComment(" ");Object.defineProperty(r,"setAttribute",{value:function(){}}),t.text=" ",t.elm=r,t.isComment=!0,t.tag=void 0,t.data=t.data||{},t.data.directives=void 0,t.componentInstance&&(t.componentInstance.$el=r),e.parentNode&&e.parentNode.replaceChild(r,e)}(t,i):"disable"===y?G.disabled=!0:"readonly"===y&&(G.readOnly=!0))};if([f.directive].concat(f.aliases).forEach(function(e){return t.directive(e,p)}),f.helper){var m="$"+f.directive;t.prototype[m]=function(e,t){var r;return(r=v).can.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))},t.prototype[m].not=function(e,t){var r;return!(r=v).can.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))},t.prototype[m].every=function(e,t){var r;return(r=v).every.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))},t.prototype[m].some=function(e,t){var r;return(r=v).some.apply(r,[u(),e,t].concat([].slice.call(arguments,2)))}}}},r=function(e){return function(t){return[e.directive].concat(e.aliases||[]).map(function(e){return t[e]}).filter(Boolean).shift()}},o=function(e){var t=e.arg,r=e.value;return[t||r[0],t?r[0]:r[1],r.slice(t?1:2)]},n=function(t){var r=t.arg,o=t.value;return[r||o[0],e.GlobalRule,r?o:o.slice(1)]},a=function(e,t,r){var o=e.arg,n=e.value,a=e.modifiers,i=o?[o,n]:n.split(" "),l=i[0],c=i[1];if(c&&a.global)throw new Error("You cannot provide verb object and use global modifier at the same time");return"string"==typeof c&&r.caseMode&&c[0].match(/[a-z]/)&&"object"==typeof t.context&&(c=t.context.$data[c]),[l,c,[]]};return t}); -//# sourceMappingURL=index.umd.js.map diff --git a/dist/index.umd.js.map b/dist/index.umd.js.map deleted file mode 100644 index 6885126..0000000 --- a/dist/index.umd.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.umd.js","sources":["../src/index.ts"],"sourcesContent":["import Acl from 'browser-acl'\nimport { Verb, VerbObject } from 'browser-acl'\nimport { VueConstructor, VNode, DirectiveFunction } from 'vue/types'\nimport { DirectiveBinding } from 'vue/types/options'\nimport VueRouter, { Route } from 'vue-router/types'\n\nimport {\n AclWithRouter,\n Behaviour,\n Options,\n PromiseChain,\n SetupCallback,\n User,\n UserGetter,\n VueAcl,\n VueRouterMeta,\n} from '../types'\n\nexport {\n AclWithRouter,\n Behaviour,\n Options,\n PromiseChain,\n SetupCallback,\n User,\n UserGetter,\n VueRouterMeta,\n} from '../types'\n\ninterface LooseHTMLElement extends HTMLElement {\n disabled: boolean\n readOnly: boolean\n}\n\nconst VueAcl: VueAcl = {\n install(\n Vue: VueConstructor,\n user: User | UserGetter,\n aclOrSetupCallback: Acl | SetupCallback | undefined = undefined,\n options: Partial = {},\n ): void {\n const userAccessor: Function =\n typeof user === 'function' ? user : () => user\n\n /* defaults */\n const strict = Boolean(options.strict)\n\n const opt: Options = Object.assign(\n {\n acl: { strict },\n aliases: ['role'],\n assumeGlobal: !strict,\n caseMode: true,\n debug: false,\n directive: 'can',\n failRoute: '/',\n helper: true,\n strict: false,\n },\n options,\n )\n\n const findCan = findCanWithOptions(opt)\n\n /* setup acl */\n let acl: AclWithRouter = new Acl(opt.acl) as AclWithRouter\n if (typeof aclOrSetupCallback === 'function') {\n aclOrSetupCallback(acl)\n } else if (aclOrSetupCallback instanceof Acl) {\n acl = aclOrSetupCallback\n }\n\n /* router init function */\n acl.router = function (router: VueRouter) {\n opt.router = router\n\n const canNavigate = (\n verb: string,\n verbObject: string | null,\n ...otherArgs: any[]\n ) => {\n return (\n (verbObject &&\n acl.can(userAccessor(), verb, verbObject, ...otherArgs)) ||\n (!verbObject && !opt.strict)\n )\n }\n\n /* convert 'edit Post' to ['edit', 'Post'] */\n const aclTuple = (value: string): [string, string | null] => {\n const [verb, verbObject = opt.assumeGlobal ? Acl.GlobalRule : null] =\n value.split(' ')\n return [verb, verbObject]\n }\n\n /**\n * chain all can-statements and functions as promises\n * each can-function must return a promise (in strict\n * mode at least). To break the chain return a none\n * true value\n */\n const chainCans = (\n metas: VueRouterMeta[],\n to: Route,\n from: Route,\n ): PromiseChain => {\n let fail: string | null = null\n const chain: PromiseChain = metas.reduce((chain, meta) => {\n return (\n chain\n .then((result: any) => {\n if (result !== true) {\n return result\n }\n\n if (typeof meta.fail === 'string') {\n fail = meta.fail\n }\n\n const can = findCan(meta)\n\n const nextPromise =\n typeof can === 'function'\n ? can(to, from, canNavigate)\n : Promise.resolve(canNavigate(...aclTuple(can)))\n\n if (opt.strict && !(nextPromise instanceof Promise)) {\n throw new Error(\n '$route.meta.can must return a promise in strict mode',\n )\n }\n\n return nextPromise\n })\n // convert errors to false\n .catch((error) => {\n if (opt.debug) {\n console.error(error)\n }\n return false\n })\n )\n }, Promise.resolve(true)) as PromiseChain\n chain.getFail = () => fail\n return chain\n }\n\n router.beforeEach((to: Route, from: Route, next: any) => {\n const metas = to.matched\n .filter((route) => route.meta && findCan(route.meta))\n .map((route) => route.meta)\n\n const chain = chainCans(metas, to, from)\n\n chain.then((result) => {\n if (result === true) {\n return next()\n }\n\n let fail: string | Function | null = chain.getFail() || opt.failRoute\n\n if (fail === '$from') {\n fail = from.path\n }\n\n next(typeof fail === 'function' ? fail(to, from) : fail)\n })\n })\n }\n\n /* init router */\n if (opt.router) {\n acl.router(opt.router)\n }\n\n /* directive update handler */\n const directiveHandler: DirectiveFunction = function (\n el: HTMLElement,\n binding: DirectiveBinding,\n vnode: VNode,\n ): void {\n const behaviour: Behaviour = getBehaviour(binding.modifiers)\n\n let verb, verbArg, verbObject, params\n verbArg = binding.arg\n\n if (Array.isArray(binding.value) && binding.expression?.startsWith('[')) {\n ;[verb, verbObject, params] = binding.modifiers.global\n ? arrayToGlobalExprTpl(binding)\n : arrayToExprTpl(binding)\n } else if (typeof binding.value === 'string') {\n ;[verb, verbObject, params] = stringToExprTpl(binding, vnode, opt)\n } else if (verbArg && typeof binding.value === 'object') {\n verb = verbArg\n verbObject = binding.value\n params = []\n } else if (\n binding.value === undefined &&\n !binding.modifiers.global &&\n opt.assumeGlobal\n ) {\n // Fall back to global if no value is provided\n verb = verbArg\n verbObject = Acl.GlobalRule\n params = []\n }\n\n if (opt.assumeGlobal && !verbObject) {\n verbObject = Acl.GlobalRule\n params = params || []\n verb = verb || verbArg\n }\n\n if (!verb || !verbObject) {\n throw new Error('Missing verb or verb object')\n }\n\n const aclMethod =\n (binding.modifiers.some && 'some') ||\n (binding.modifiers.every && 'every') ||\n 'can'\n\n const ok = acl[aclMethod](userAccessor(), verb, verbObject, ...params)\n const not = binding.modifiers.not\n\n const el_ = el as LooseHTMLElement\n\n if (!el.hasAttribute('disabled')) {\n el_.disabled = false\n }\n\n if (!el.hasAttribute('readOnly')) {\n el_.readOnly = false\n }\n\n if ((ok && not) || (!ok && !not)) {\n if (behaviour === 'hide') {\n commentNode(el, vnode)\n } else if (behaviour === 'disable') {\n el_.disabled = true\n } else if (behaviour === 'readonly') {\n el_.readOnly = true\n }\n }\n }\n\n /* set up directive for 'can' and aliases */\n const directiveNames = [opt.directive, ...opt.aliases]\n directiveNames.forEach((name) => Vue.directive(name, directiveHandler))\n\n /* define helpers */\n if (opt.helper) {\n const helper = `$${opt.directive}`\n /* @type AclHelper */\n Vue.prototype[helper] = function (\n verb: Verb,\n verbObject: VerbObject,\n ...args: any[]\n ) {\n return acl.can(userAccessor(), verb, verbObject, ...args)\n }\n /* @type AclHelper */\n Vue.prototype[helper].not = function (\n verb: Verb,\n verbObject: VerbObject,\n ...args: any[]\n ) {\n return !acl.can(userAccessor(), verb, verbObject, ...args)\n }\n /* @type AclHelperMany */\n Vue.prototype[helper].every = function (\n verb: Verb,\n verbObjects: VerbObject[],\n ...args: any[]\n ) {\n return acl.every(userAccessor(), verb, verbObjects, ...args)\n }\n /* @type AclHelperMany */\n Vue.prototype[helper].some = function (\n verb: Verb,\n verbObjects: VerbObject[],\n ...args: any[]\n ) {\n return acl.some(userAccessor(), verb, verbObjects, ...args)\n }\n }\n },\n}\n\nfunction getBehaviour(modifiers: any): Behaviour {\n if (typeof modifiers.disable !== 'undefined') {\n return 'disable'\n }\n if (typeof modifiers.readonly !== 'undefined') {\n return 'readonly'\n }\n return 'hide'\n}\n\n/**\n * Create comment node\n *\n * @private\n * @author https://stackoverflow.com/questions/43003976/a-custom-directive-similar-to-v-if-in-vuejs#43543814\n */\nfunction commentNode(el: HTMLElement, vnode: VNode) {\n const comment = document.createComment(' ')\n\n Object.defineProperty(comment, 'setAttribute', {\n value: () => undefined,\n })\n\n vnode.text = ' '\n vnode.elm = comment\n vnode.isComment = true\n vnode.tag = undefined\n\n vnode.data = vnode.data || {}\n vnode.data.directives = undefined\n\n if (vnode.componentInstance) {\n // @ts-ignore\n vnode.componentInstance.$el = comment\n }\n\n if (el.parentNode) {\n el.parentNode.replaceChild(comment, el)\n }\n}\n\n/**\n * Return the first property from meta that is 'can' or one of its aliases.\n */\nconst findCanWithOptions =\n (opt: Options) =>\n (meta: VueRouterMeta): string | Function => {\n return ([opt.directive, ...(opt.aliases || [])] as string[])\n .map((key: string) => meta[key])\n .filter(Boolean)\n .shift()\n }\n\n/**\n * Maps binding.value of type array to expression tuple\n */\nconst arrayToExprTpl = ({ arg, value }: DirectiveBinding) => [\n arg || value[0],\n arg ? value[0] : value[1],\n arg ? value.slice(1) : value.slice(2),\n]\n\n/**\n * Maps binding.value of type array to global expression tuple\n */\nconst arrayToGlobalExprTpl = ({ arg, value }: DirectiveBinding) => [\n arg || value[0],\n Acl.GlobalRule,\n arg ? value : value.slice(1),\n]\n\n/**\n * Maps binding.value of type string to expression tuple\n */\nconst stringToExprTpl = (\n { arg, value, modifiers }: DirectiveBinding,\n vnode: VNode,\n opt: Options,\n) => {\n let [verb, verbObject] = arg ? [arg, value] : value.split(' ')\n\n if (verbObject && modifiers.global) {\n throw new Error(\n 'You cannot provide verb object and use global modifier at the same time',\n )\n }\n\n if (\n typeof verbObject === 'string' &&\n opt.caseMode &&\n verbObject[0].match(/[a-z]/) &&\n typeof vnode.context === 'object'\n ) {\n verbObject = vnode.context.$data[verbObject]\n }\n\n return [verb, verbObject, []]\n}\n\nexport default VueAcl\n"],"names":["VueAcl","install","Vue","user","aclOrSetupCallback","options","undefined","userAccessor","strict","Boolean","opt","Object","assign","acl","aliases","assumeGlobal","caseMode","debug","directive","failRoute","helper","findCan","findCanWithOptions","Acl","router","canNavigate","verb","verbObject","can","beforeEach","to","from","next","chain","metas","fail","reduce","meta","then","result","nextPromise","Promise","resolve","split","GlobalRule","Error","catch","error","console","getFail","chainCans","matched","filter","route","map","path","directiveHandler","el","binding","vnode","verbArg","params","modifiers","behaviour","disable","readonly","arg","Array","isArray","value","expression","_binding$expression","startsWith","global","arrayToGlobalExprTpl","arrayToExprTpl","stringToExprTpl","ok","some","every","not","el_","hasAttribute","disabled","readOnly","comment","document","createComment","defineProperty","text","elm","isComment","tag","data","directives","componentInstance","$el","parentNode","replaceChild","commentNode","forEach","name","prototype","verbObjects","key","shift","slice","match","context","$data"],"mappings":"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"} \ No newline at end of file diff --git a/package.json b/package.json index 2e5706e..3ec0839 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vue-browser-acl", "description": "Easy ACL in Vue build on top of the browser-acl package.", "author": "Michael Bøcker-Larsen ", - "version": "1.0.0", + "version": "3.0.0", "keywords": [ "vue", "plugin", @@ -28,27 +28,20 @@ "prebuild": "rm -rf ./dist", "build": "microbundle build --tsconfig ./tsconfig.json --name VueBrowserAcl", "build:watch": "microbundle watch --tsconfig ./tsconfig.json --name VueBrowserAcl", - "test": "jest --config jest.config.json", - "test:watch": "jest --config jest.config.json --watchAll", "version": "npm run build && git add -A ./dist", "postversion": "git push && git push --tags" }, "dependencies": { - "browser-acl": "^1.0.0" + "browser-acl": "^1.0.1" }, "devDependencies": { - "@babel/core": "^7.9.6", - "@babel/preset-env": "^7.9.6", - "@nuxt/types": "^0.7.6", - "@types/jest": "^25.2.3", - "@vue/test-utils": "^1.0.3", - "babel-jest": "^26.0.1", - "jest": "^26.0.1", - "microbundle": "^0.12.0", - "ts-jest": "^26.0.0", - "typescript": "^3.9.3", - "vue": "^2.6.11", - "vue-router": "^3.2.0", + "@babel/core": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@nuxt/types": "^2.15.8", + "microbundle": "^0.15.0", + "typescript": "^4.7.4", + "vue": "^3.2.37", + "vue-router": "^4.0.16", "vue-template-compiler": "^2.6.11" }, "prettier": { diff --git a/src/decode.ts b/src/decode.ts new file mode 100644 index 0000000..f18191e --- /dev/null +++ b/src/decode.ts @@ -0,0 +1,121 @@ +import Acl from 'browser-acl' +import { DirectiveBinding, VNode } from 'vue' +import { Behaviour, Options } from '../types' + +export type SyntaxDecoder = (ctx: BindingCtx) => DecodedSyntax | undefined + +type BindingCtx = { + verbArg: any + binding: DirectiveBinding + vnode: VNode + opt: Options +} + +export type DecodedSyntax = { + verb: string + verbObject: any + params: any[] +} + +/** + * Creates a decode syntax pipeline + */ +const decodePipe = (decoders: SyntaxDecoder[]) => (ctx: BindingCtx) => { + for (let decoder of decoders) { + const decoded = decoder(ctx) + if (decoded) return decoded + } +} + +/** + * Decodes the syntax of the directive using a pipe of + * decoders. The first decoder that returns a value + * dertermines the syntax. + */ +export const decodeSyntax = decodePipe([ + // String syntax is used + (ctx: BindingCtx) => { + const { binding, vnode, opt } = ctx + if (typeof binding.value === 'string') { + const [verb, verbObject, params] = stringToExprTpl(binding, vnode, opt) + return { verb, verbObject, params } + } + }, + // Argument syntax is used + (ctx: BindingCtx) => { + const { verbArg, binding } = ctx + if (verbArg && typeof binding.value === 'object') { + return { verb: verbArg, verbObject: binding.value, params: [] } + } + }, + (ctx: BindingCtx) => { + const { binding, opt, verbArg } = ctx + if ( + binding.value === undefined && + !binding.modifiers.global && + opt.assumeGlobal + ) { + // Interpret as global rule if no value is provided + return { verb: verbArg, verbObject: Acl.GlobalRule, params: [] } + } + }, +]) + +/** + * Maps binding.value of type string to expression tuple + */ +const stringToExprTpl = ( + { arg, value, modifiers }: DirectiveBinding, + vnode: VNode, + opt: Options, +) => { + let [verb, verbObject] = arg ? [arg, value] : value.split(' ') + + if (verbObject && modifiers.global) { + throw new Error( + 'You cannot provide verb object and use global modifier at the same time', + ) + } + + if ( + typeof verbObject === 'string' && + opt.caseMode && + verbObject[0].match(/[a-z]/) && + // @ts-ignore + typeof vnode.context === 'object' + ) { + // @ts-ignore + verbObject = vnode.context.$data[verbObject] + } + + return [verb, verbObject, []] +} + +/** + * Determine which modifier is used + * + * disable = will still show the element but will disable it + * readonly = will still show the element but will make it readonly + * hide = will hide the element + */ +export function getBehaviour( + modifiers: DirectiveBinding['modifiers'], +): Behaviour { + if (typeof modifiers.disable !== 'undefined') { + return 'disable' + } + if (typeof modifiers.readonly !== 'undefined') { + return 'readonly' + } + return 'hide' +} + +export function getAclMethod( + binding: DirectiveBinding, +): 'can' | 'some' | 'every' { + return ( + (binding.modifiers.some && 'some') || + (binding.modifiers.every && 'every') || + 'can' + ) +} diff --git a/src/index.ts b/src/index.ts index 7d574cb..6edfca6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,25 +1,27 @@ import Acl from 'browser-acl' -import { Verb, VerbObject } from 'browser-acl' -import { VueConstructor, VNode, DirectiveFunction } from 'vue/types' -import { DirectiveBinding } from 'vue/types/options' -import VueRouter, { Route } from 'vue-router/types' - +// import { Verb, VerbObject } from 'browser-acl' +import { Plugin, DirectiveBinding, FunctionDirective, VNode, App } from 'vue' +import { + DecodedSyntax, + decodeSyntax, + getAclMethod, + getBehaviour, +} from './decode' +import { makeComment } from './makeComment' import { AclWithRouter, Behaviour, Options, - PromiseChain, SetupCallback, User, UserGetter, VueAcl, - VueRouterMeta, } from '../types' +import { applyDefaults } from './options' export { AclWithRouter, Behaviour, - Options, PromiseChain, SetupCallback, User, @@ -32,9 +34,9 @@ interface LooseHTMLElement extends HTMLElement { readOnly: boolean } -const VueAcl: VueAcl = { +const VueAcl: Plugin = { install( - Vue: VueConstructor, + app: App, user: User | UserGetter, aclOrSetupCallback: Acl | SetupCallback | undefined = undefined, options: Partial = {}, @@ -42,27 +44,10 @@ const VueAcl: VueAcl = { const userAccessor: Function = typeof user === 'function' ? user : () => user - /* defaults */ - const strict = Boolean(options.strict) - - const opt: Options = Object.assign( - { - acl: { strict }, - aliases: ['role'], - assumeGlobal: !strict, - caseMode: true, - debug: false, - directive: 'can', - failRoute: '/', - helper: true, - strict: false, - }, - options, - ) + const opt = applyDefaults(options) + // const strict = opt.strict - const findCan = findCanWithOptions(opt) - - /* setup acl */ + // Configure ACL let acl: AclWithRouter = new Acl(opt.acl) as AclWithRouter if (typeof aclOrSetupCallback === 'function') { aclOrSetupCallback(acl) @@ -70,320 +55,112 @@ const VueAcl: VueAcl = { acl = aclOrSetupCallback } - /* router init function */ - acl.router = function (router: VueRouter) { - opt.router = router - - const canNavigate = ( - verb: string, - verbObject: string | null, - ...otherArgs: any[] - ) => { - return ( - (verbObject && - acl.can(userAccessor(), verb, verbObject, ...otherArgs)) || - (!verbObject && !opt.strict) - ) - } - - /* convert 'edit Post' to ['edit', 'Post'] */ - const aclTuple = (value: string): [string, string | null] => { - const [verb, verbObject = opt.assumeGlobal ? Acl.GlobalRule : null] = - value.split(' ') - return [verb, verbObject] - } - - /** - * chain all can-statements and functions as promises - * each can-function must return a promise (in strict - * mode at least). To break the chain return a none - * true value - */ - const chainCans = ( - metas: VueRouterMeta[], - to: Route, - from: Route, - ): PromiseChain => { - let fail: string | null = null - const chain: PromiseChain = metas.reduce((chain, meta) => { - return ( - chain - .then((result: any) => { - if (result !== true) { - return result - } - - if (typeof meta.fail === 'string') { - fail = meta.fail - } - - const can = findCan(meta) - - const nextPromise = - typeof can === 'function' - ? can(to, from, canNavigate) - : Promise.resolve(canNavigate(...aclTuple(can))) - - if (opt.strict && !(nextPromise instanceof Promise)) { - throw new Error( - '$route.meta.can must return a promise in strict mode', - ) - } - - return nextPromise - }) - // convert errors to false - .catch((error) => { - if (opt.debug) { - console.error(error) - } - return false - }) - ) - }, Promise.resolve(true)) as PromiseChain - chain.getFail = () => fail - return chain - } - - router.beforeEach((to: Route, from: Route, next: any) => { - const metas = to.matched - .filter((route) => route.meta && findCan(route.meta)) - .map((route) => route.meta) - - const chain = chainCans(metas, to, from) - - chain.then((result) => { - if (result === true) { - return next() - } - - let fail: string | Function | null = chain.getFail() || opt.failRoute - - if (fail === '$from') { - fail = from.path - } - - next(typeof fail === 'function' ? fail(to, from) : fail) - }) - }) - } - - /* init router */ - if (opt.router) { - acl.router(opt.router) - } - - /* directive update handler */ - const directiveHandler: DirectiveFunction = function ( - el: HTMLElement, + // Define directive handler + const handler: FunctionDirective = function ( + el, binding: DirectiveBinding, - vnode: VNode, + vnode, ): void { const behaviour: Behaviour = getBehaviour(binding.modifiers) - let verb, verbArg, verbObject, params - verbArg = binding.arg - - if (Array.isArray(binding.value) && binding.expression?.startsWith('[')) { - ;[verb, verbObject, params] = binding.modifiers.global - ? arrayToGlobalExprTpl(binding) - : arrayToExprTpl(binding) - } else if (typeof binding.value === 'string') { - ;[verb, verbObject, params] = stringToExprTpl(binding, vnode, opt) - } else if (verbArg && typeof binding.value === 'object') { - verb = verbArg - verbObject = binding.value - params = [] - } else if ( - binding.value === undefined && - !binding.modifiers.global && - opt.assumeGlobal - ) { - // Fall back to global if no value is provided - verb = verbArg - verbObject = Acl.GlobalRule - params = [] - } - - if (opt.assumeGlobal && !verbObject) { - verbObject = Acl.GlobalRule - params = params || [] - verb = verb || verbArg - } + const syntax = decodeSyntax({ + binding, + verbArg: binding.arg, + vnode, + opt, + }) - if (!verb || !verbObject) { + if (!syntax) { + // TODO is !syntax the same as (!verb || !verbObject) { throw new Error('Missing verb or verb object') } - const aclMethod = - (binding.modifiers.some && 'some') || - (binding.modifiers.every && 'every') || - 'can' - - const ok = acl[aclMethod](userAccessor(), verb, verbObject, ...params) - const not = binding.modifiers.not - - const el_ = el as LooseHTMLElement - - if (!el.hasAttribute('disabled')) { - el_.disabled = false - } - - if (!el.hasAttribute('readOnly')) { - el_.readOnly = false - } + const { ok, not } = applyAcl(acl, syntax, binding, userAccessor()) - if ((ok && not) || (!ok && !not)) { - if (behaviour === 'hide') { - commentNode(el, vnode) - } else if (behaviour === 'disable') { - el_.disabled = true - } else if (behaviour === 'readonly') { - el_.readOnly = true - } - } + applyBehaviour(el as LooseHTMLElement, vnode, behaviour, ok, not) } - /* set up directive for 'can' and aliases */ - const directiveNames = [opt.directive, ...opt.aliases] - directiveNames.forEach((name) => Vue.directive(name, directiveHandler)) + app.directive('can', handler) /* define helpers */ - if (opt.helper) { - const helper = `$${opt.directive}` - /* @type AclHelper */ - Vue.prototype[helper] = function ( - verb: Verb, - verbObject: VerbObject, - ...args: any[] - ) { - return acl.can(userAccessor(), verb, verbObject, ...args) - } - /* @type AclHelper */ - Vue.prototype[helper].not = function ( - verb: Verb, - verbObject: VerbObject, - ...args: any[] - ) { - return !acl.can(userAccessor(), verb, verbObject, ...args) - } - /* @type AclHelperMany */ - Vue.prototype[helper].every = function ( - verb: Verb, - verbObjects: VerbObject[], - ...args: any[] - ) { - return acl.every(userAccessor(), verb, verbObjects, ...args) - } - /* @type AclHelperMany */ - Vue.prototype[helper].some = function ( - verb: Verb, - verbObjects: VerbObject[], - ...args: any[] - ) { - return acl.some(userAccessor(), verb, verbObjects, ...args) - } - } + // if (opt.helper) { + // const helper = `$${opt.directive}` + // /* @type AclHelper */ + // Vue.prototype[helper] = function ( + // verb: Verb, + // verbObject: VerbObject, + // ...args: any[] + // ) { + // return acl.can(userAccessor(), verb, verbObject, ...args) + // } + // /* @type AclHelper */ + // Vue.prototype[helper].not = function ( + // verb: Verb, + // verbObject: VerbObject, + // ...args: any[] + // ) { + // return !acl.can(userAccessor(), verb, verbObject, ...args) + // } + // /* @type AclHelperMany */ + // Vue.prototype[helper].every = function ( + // verb: Verb, + // verbObjects: VerbObject[], + // ...args: any[] + // ) { + // return acl.every(userAccessor(), verb, verbObjects, ...args) + // } + // /* @type AclHelperMany */ + // Vue.prototype[helper].some = function ( + // verb: Verb, + // verbObjects: VerbObject[], + // ...args: any[] + // ) { + // return acl.some(userAccessor(), verb, verbObjects, ...args) + // } + // } }, } -function getBehaviour(modifiers: any): Behaviour { - if (typeof modifiers.disable !== 'undefined') { - return 'disable' +function applyAcl( + acl: AclWithRouter, + syntax: DecodedSyntax, + binding: DirectiveBinding, + user: User, +) { + const method = getAclMethod(binding) + const { verb, verbObject, params } = syntax + const ok = acl[method](user, verb, verbObject, ...params) + const not = binding.modifiers.not + return { + ok, + not, } - if (typeof modifiers.readonly !== 'undefined') { - return 'readonly' - } - return 'hide' } -/** - * Create comment node - * - * @private - * @author https://stackoverflow.com/questions/43003976/a-custom-directive-similar-to-v-if-in-vuejs#43543814 - */ -function commentNode(el: HTMLElement, vnode: VNode) { - const comment = document.createComment(' ') - - Object.defineProperty(comment, 'setAttribute', { - value: () => undefined, - }) - - vnode.text = ' ' - vnode.elm = comment - vnode.isComment = true - vnode.tag = undefined - - vnode.data = vnode.data || {} - vnode.data.directives = undefined - - if (vnode.componentInstance) { - // @ts-ignore - vnode.componentInstance.$el = comment - } - - if (el.parentNode) { - el.parentNode.replaceChild(comment, el) - } -} - -/** - * Return the first property from meta that is 'can' or one of its aliases. - */ -const findCanWithOptions = - (opt: Options) => - (meta: VueRouterMeta): string | Function => { - return ([opt.directive, ...(opt.aliases || [])] as string[]) - .map((key: string) => meta[key]) - .filter(Boolean) - .shift() - } - -/** - * Maps binding.value of type array to expression tuple - */ -const arrayToExprTpl = ({ arg, value }: DirectiveBinding) => [ - arg || value[0], - arg ? value[0] : value[1], - arg ? value.slice(1) : value.slice(2), -] - -/** - * Maps binding.value of type array to global expression tuple - */ -const arrayToGlobalExprTpl = ({ arg, value }: DirectiveBinding) => [ - arg || value[0], - Acl.GlobalRule, - arg ? value : value.slice(1), -] - -/** - * Maps binding.value of type string to expression tuple - */ -const stringToExprTpl = ( - { arg, value, modifiers }: DirectiveBinding, +function applyBehaviour( + el: LooseHTMLElement, vnode: VNode, - opt: Options, -) => { - let [verb, verbObject] = arg ? [arg, value] : value.split(' ') - - if (verbObject && modifiers.global) { - throw new Error( - 'You cannot provide verb object and use global modifier at the same time', - ) + behaviour: Behaviour, + ok: boolean, + not: boolean, +): void { + if (!el.hasAttribute('disabled')) { + el.disabled = false } - if ( - typeof verbObject === 'string' && - opt.caseMode && - verbObject[0].match(/[a-z]/) && - typeof vnode.context === 'object' - ) { - verbObject = vnode.context.$data[verbObject] + if (!el.hasAttribute('readOnly')) { + el.readOnly = false } - return [verb, verbObject, []] + if ((ok && not) || (!ok && !not)) { + if (behaviour === 'hide') { + makeComment(el, vnode) + } else if (behaviour === 'disable') { + el.disabled = true + } else if (behaviour === 'readonly') { + el.readOnly = true + } + } } export default VueAcl diff --git a/src/makeComment.ts b/src/makeComment.ts new file mode 100644 index 0000000..51a0bf4 --- /dev/null +++ b/src/makeComment.ts @@ -0,0 +1,45 @@ +import { VNode } from 'vue' + +type VueVNode = VNode & { + text: any + isComment: any + elm: Comment + tag?: any + data: any + directives: any + componentInstance?: { + $el: Comment + } +} + +/** + * Create comment node + * + * https://stackoverflow.com/questions/43003976/a-custom-directive-similar-to-v-if-in-vuejs#43543814 + * + * @private + */ +export function makeComment(el: HTMLElement, vnode: VNode) { + const node = vnode as VueVNode + const comment = document.createComment(' ') + + Object.defineProperty(comment, 'setAttribute', { + value: () => undefined, + }) + + node.text = ' ' + node.elm = comment + node.isComment = true + node.tag = undefined + + node.data = node.data || {} + node.data.directives = undefined + + if (node.componentInstance) { + node.componentInstance.$el = comment + } + + if (el.parentNode) { + el.parentNode.replaceChild(comment, el) + } +} diff --git a/src/options.ts b/src/options.ts new file mode 100644 index 0000000..3e53f95 --- /dev/null +++ b/src/options.ts @@ -0,0 +1,20 @@ +import { Options } from '../types' + +export const applyDefaults = (options: Partial = {}): Options => { + const strict = Boolean(options.strict) + + return Object.assign( + { + acl: { strict }, + aliases: ['role'], + assumeGlobal: !strict, + caseMode: true, + debug: false, + directive: 'can', + failRoute: '/', + helper: true, + strict: false, + }, + options, + ) +} diff --git a/src/router.ts b/src/router.ts new file mode 100644 index 0000000..cffb5c2 --- /dev/null +++ b/src/router.ts @@ -0,0 +1,116 @@ +// const findCan = findCanWithOptions(opt) + +/* router init function */ +// acl.router = function (router: VueRouter) { +// opt.router = router + +// const canNavigate = ( +// verb: string, +// verbObject: string | null, +// ...otherArgs: any[] +// ) => { +// return ( +// (verbObject && +// acl.can(userAccessor(), verb, verbObject, ...otherArgs)) || +// (!verbObject && !opt.strict) +// ) +// } + +// /* convert 'edit Post' to ['edit', 'Post'] */ +// const aclTuple = (value: string): [string, string | null] => { +// const [verb, verbObject = opt.assumeGlobal ? Acl.GlobalRule : null] = +// value.split(' ') +// return [verb, verbObject] +// } + +// /** +// * chain all can-statements and functions as promises +// * each can-function must return a promise (in strict +// * mode at least). To break the chain return a none +// * true value +// */ +// const chainCans = ( +// metas: VueRouterMeta[], +// to: Route, +// from: Route, +// ): PromiseChain => { +// let fail: string | null = null +// const chain: PromiseChain = metas.reduce((chain, meta) => { +// return ( +// chain +// .then((result: any) => { +// if (result !== true) { +// return result +// } + +// if (typeof meta.fail === 'string') { +// fail = meta.fail +// } + +// const can = findCan(meta) + +// const nextPromise = +// typeof can === 'function' +// ? can(to, from, canNavigate) +// : Promise.resolve(canNavigate(...aclTuple(can))) + +// if (opt.strict && !(nextPromise instanceof Promise)) { +// throw new Error( +// '$route.meta.can must return a promise in strict mode', +// ) +// } + +// return nextPromise +// }) +// // convert errors to false +// .catch((error) => { +// if (opt.debug) { +// console.error(error) +// } +// return false +// }) +// ) +// }, Promise.resolve(true)) as PromiseChain +// chain.getFail = () => fail +// return chain +// } + +// router.beforeEach((to: Route, from: Route, next: any) => { +// const metas = to.matched +// .filter((route) => route.meta && findCan(route.meta)) +// .map((route) => route.meta) + +// const chain = chainCans(metas, to, from) + +// chain.then((result) => { +// if (result === true) { +// return next() +// } + +// let fail: string | Function | null = chain.getFail() || opt.failRoute + +// if (fail === '$from') { +// fail = from.path +// } + +// next(typeof fail === 'function' ? fail(to, from) : fail) +// }) +// }) +// } + +/* init router */ +// if (opt.router) { +// acl.router(opt.router) +// } + +/** + * Return the first property from meta that is 'can' or one of its aliases. + */ +// const findCanWithOptions = +// (opt: Options) => +// (meta: VueRouterMeta): string | Function => { +// return ([opt.directive, ...(opt.aliases || [])] as string[]) +// .map((key: string) => meta[key]) +// .filter(Boolean) +// .shift() +// }