Skip to content

Commit 8d04f41

Browse files
authored
refactor!: types (#23)
1 parent cd9cc96 commit 8d04f41

File tree

10 files changed

+42
-40
lines changed

10 files changed

+42
-40
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ export default defineNuxtPlugin({
108108

109109
Nitro plugin:
110110

111-
112111
```ts
113112
export default defineNitroPlugin((nitroApp) => {
114113
nitroApp.hooks.hook('request', async (event) => {
@@ -151,7 +150,7 @@ If you have many abilities, you could prefer to create a directory `utils/abilit
151150
By default, guests are not allowed to perform any action and the ability is not called. This behavior can be changed per ability:
152151

153152
```ts
154-
export const listPosts = defineAbility({ allowGuests: true }, (user: User | null) => true)
153+
export const listPosts = defineAbility({ allowGuest: true }, (user: User | null) => true)
155154
```
156155

157156
Now, unauthenticated users can list posts.

src/runtime/components/Bouncer.vue

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
<script lang="ts" setup generic="Ability extends BouncerAbility<any>">
2-
import type { AuthorizerResponse, BouncerAbility } from '../../utils'
2+
import type { BouncerAbility, BouncerArgs } from '../../utils'
33
import { allows, ref, watchEffect } from '#imports'
44
5-
type PropsArgs = Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never
6-
75
const props = defineProps<{
86
bouncerAbility: Ability
9-
args?: PropsArgs
7+
args?: BouncerArgs<Ability>
108
}>()
119
1210
const can = ref(await resolve())
@@ -17,7 +15,7 @@ watchEffect(async () => {
1715
})
1816
1917
async function resolve() {
20-
return await allows(props.bouncerAbility, ...(props.args ?? [] as unknown as PropsArgs))
18+
return await allows(props.bouncerAbility, ...(props.args ?? [] as any))
2119
}
2220
</script>
2321

src/runtime/components/Can.vue

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
<script lang="ts" setup generic="Ability extends BouncerAbility<any>">
2-
import type { AuthorizerResponse, BouncerAbility } from '../../utils'
2+
import type { BouncerArgs, BouncerAbility } from '../../utils'
33
import { allows, ref, watchEffect } from '#imports'
44
5-
type PropsArgs = Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never
6-
75
const props = defineProps<{
86
bouncerAbility: Ability
9-
args?: PropsArgs
7+
args?: BouncerArgs<Ability>
108
}>()
119
1210
const can = ref(await resolve())
@@ -17,14 +15,12 @@ watchEffect(async () => {
1715
})
1816
1917
async function resolve() {
20-
return await allows(props.bouncerAbility, ...(props.args ?? [] as unknown as PropsArgs))
18+
return await allows(props.bouncerAbility, ...(props.args ?? [] as any))
2119
}
2220
</script>
2321

2422
<template>
25-
<template
26-
v-if="can"
27-
>
23+
<template v-if="can">
2824
<slot />
2925
</template>
3026
</template>

src/runtime/components/Cannot.vue

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
<script lang="ts" setup generic="Ability extends BouncerAbility<any>">
2-
import type { AuthorizerResponse, BouncerAbility } from '../../utils'
2+
import type { BouncerAbility, BouncerArgs } from '../../utils'
33
import { denies, ref, watchEffect } from '#imports'
44
5-
type PropsArgs = Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never
6-
75
const props = defineProps<{
86
bouncerAbility: Ability
9-
args?: PropsArgs
7+
args?: BouncerArgs<Ability>
108
}>()
119
1210
const cannot = ref(await resolve())
@@ -17,14 +15,12 @@ watchEffect(async () => {
1715
})
1816
1917
async function resolve() {
20-
return await denies(props.bouncerAbility, ...(props.args ?? [] as unknown as PropsArgs))
18+
return await denies(props.bouncerAbility, ...(props.args ?? [] as any))
2119
}
2220
</script>
2321

2422
<template>
25-
<template
26-
v-if="cannot"
27-
>
23+
<template v-if="cannot">
2824
<slot />
2925
</template>
3026
</template>

src/runtime/nuxt.d.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
declare module '#app' {
22
interface NuxtApp {
33
$authorization: {
4-
resolveClientUser: () => Promise<any>
4+
resolveClientUser: <User extends Record<string, any>>() => Promise<User | null>
55
}
66
}
77
}
88

99
declare module 'vue' {
1010
interface ComponentCustomProperties {
1111
$authorization: {
12-
resolveClientUser: () => Promise<any>
12+
resolveClientUser: <User extends Record<string, any>>() => Promise<User | null>
13+
}
14+
}
15+
}
16+
17+
declare module 'h3' {
18+
interface H3EventContext {
19+
$authorization: {
20+
resolveServerUser: <User extends Record<string, any>>() => Promise<User | null>
1321
}
1422
}
1523
}

src/runtime/server/utils/bouncer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { H3Event } from 'h3'
2-
import type { AuthorizerResponse, BouncerAbility } from '../../../utils'
2+
import type { BouncerAbility, BouncerArgs } from '../../../utils'
33
import { allows as _allows, denies as _denies, authorize as _authorize, AuthorizationError } from '../../../utils'
44
import { createError } from '#imports'
55

66
/**
77
* Allows a user to perform an action based on their role and the data.
88
*/
9-
export async function allows<Ability extends BouncerAbility<any>>(event: H3Event, bouncerAbility: Ability, ...args: Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never): Promise<boolean> {
9+
export async function allows<Event extends H3Event, Ability extends BouncerAbility<any>>(event: Event, bouncerAbility: Ability, ...args: BouncerArgs<Ability>): Promise<boolean> {
1010
const user = await event.context.$authorization.resolveServerUser()
1111

1212
return _allows(bouncerAbility, user, ...args)
@@ -15,7 +15,7 @@ export async function allows<Ability extends BouncerAbility<any>>(event: H3Event
1515
/**
1616
* Denies a user to perform an action based on their role and the data.
1717
*/
18-
export async function denies<Ability extends BouncerAbility<any>>(event: H3Event, bouncerAbility: Ability, ...args: Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never): Promise<boolean> {
18+
export async function denies<Event extends H3Event, Ability extends BouncerAbility<any>>(event: Event, bouncerAbility: Ability, ...args: BouncerArgs<Ability>): Promise<boolean> {
1919
const user = await event.context.$authorization.resolveServerUser()
2020

2121
return _denies(bouncerAbility, user, ...args)
@@ -24,7 +24,7 @@ export async function denies<Ability extends BouncerAbility<any>>(event: H3Event
2424
/**
2525
* Throws an error if the user is not allowed to perform an action.
2626
*/
27-
export async function authorize<Ability extends BouncerAbility<any>>(event: H3Event, bouncerAbility: Ability, ...args: Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never): Promise<void> {
27+
export async function authorize<Event extends H3Event, Ability extends BouncerAbility<any>>(event: Event, bouncerAbility: Ability, ...args: BouncerArgs<Ability>): Promise<void> {
2828
try {
2929
const user = await event.context.$authorization.resolveServerUser()
3030

src/runtime/utils/bouncer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type { BouncerAbility, AuthorizerResponse } from '../../utils'
1+
import type { BouncerAbility, BouncerArgs } from '../../utils'
22
import { allows as _allows, denies as _denies, authorize as _authorize, AuthorizationError } from '../../utils'
33
import { useNuxtApp, createError } from '#imports'
44

55
/**
66
* Client side utility to check if a user can perform an action.
77
*/
8-
export async function allows<Ability extends BouncerAbility<any>>(bouncerAbility: Ability, ...args: Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never): Promise<boolean> {
8+
export async function allows<Ability extends BouncerAbility<any>>(bouncerAbility: Ability, ...args: BouncerArgs<Ability>): Promise<boolean> {
99
const user = await useNuxtApp().$authorization.resolveClientUser()
1010

1111
return _allows(bouncerAbility, user, ...args)
@@ -14,7 +14,7 @@ export async function allows<Ability extends BouncerAbility<any>>(bouncerAbility
1414
/**
1515
* Client side utility to check if a user cannot perform an action.
1616
*/
17-
export async function denies<Ability extends BouncerAbility<any>>(bouncerAbility: Ability, ...args: Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never): Promise<boolean> {
17+
export async function denies<Ability extends BouncerAbility<any>>(bouncerAbility: Ability, ...args: BouncerArgs<Ability>): Promise<boolean> {
1818
const user = await useNuxtApp().$authorization.resolveClientUser()
1919

2020
return _denies(bouncerAbility, user, ...args)
@@ -23,7 +23,7 @@ export async function denies<Ability extends BouncerAbility<any>>(bouncerAbility
2323
/**
2424
* Client side utility to throw an error if a user is not allowed to perform an action.
2525
*/
26-
export async function authorize<Ability extends BouncerAbility<any>>(bouncerAbility: Ability, ...args: Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never): Promise<void> {
26+
export async function authorize<Ability extends BouncerAbility<any>>(bouncerAbility: Ability, ...args: BouncerArgs<Ability>): Promise<void> {
2727
try {
2828
const user = await useNuxtApp().$authorization.resolveClientUser()
2929

src/utils/ability.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type AuthorizerToAbility<Authorizer> = Authorizer extends (
1010
? {
1111
allowGuest: boolean
1212
original: Authorizer
13-
execute(user: User, ...args: Args): AuthorizerResponse
13+
execute(user: User | null, ...args: Args): AuthorizerResponse
1414
}
1515
: never
1616

src/utils/bouncer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createAuthorizationError } from './error'
2-
import type { AuthorizationResponse, AuthorizerResponse, BouncerAbility } from './types'
2+
import type { AuthorizationResponse, BouncerAbility, BouncerArgs } from './types'
33

44
/**
55
* Normalize the ability execution result to an authorization response.
@@ -15,23 +15,23 @@ export function normalizeAuthorizationResponse(result: boolean | AuthorizationRe
1515
/**
1616
* Check if a user can perform an action.
1717
*/
18-
export async function allows<Ability extends BouncerAbility<any>, User>(bouncerAbility: Ability, user: User, ...args: Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never): Promise<boolean> {
18+
export async function allows<Ability extends BouncerAbility<any>, User extends Record<string, any> = Ability extends BouncerAbility<infer U> ? U : never>(bouncerAbility: Ability, user: User | null, ...args: BouncerArgs<Ability>): Promise<boolean> {
1919
const response = await bouncerAbility.execute(user, ...args)
2020
return normalizeAuthorizationResponse(response).authorized
2121
}
2222

2323
/**
2424
* Check if a user cannot perform an action.
2525
*/
26-
export async function denies<Ability extends BouncerAbility<any>, User>(bouncerAbility: Ability, user: User, ...args: Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never): Promise<boolean> {
26+
export async function denies<Ability extends BouncerAbility<any>, User extends Record<string, any> = Ability extends BouncerAbility<infer U> ? U : never>(bouncerAbility: Ability, user: User | null, ...args: BouncerArgs<Ability>): Promise<boolean> {
2727
const response = await bouncerAbility.execute(user, ...args)
2828
return !normalizeAuthorizationResponse(response).authorized
2929
}
3030

3131
/**
3232
* Check if a user can perform an action and throws an error if not.
3333
*/
34-
export async function authorize<Ability extends BouncerAbility<any>, User>(bouncerAbility: Ability, user: User, ...args: Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never): Promise<void> {
34+
export async function authorize<Ability extends BouncerAbility<any>, User extends Record<string, any> = Ability extends BouncerAbility<infer U> ? U : never>(bouncerAbility: Ability, user: User | null, ...args: BouncerArgs<Ability>): Promise<void> {
3535
const response = await bouncerAbility.execute(user, ...args)
3636
const normalized = normalizeAuthorizationResponse(response)
3737
if (!normalized.authorized) {

src/utils/types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@ export type AuthorizerResponse =
1919
/**
2020
* Represents the authorizer function that will be executed to determine if a user is authorized to perform an action.
2121
*/
22-
export type BouncerAuthorizer<User> = (user: User, ...args: any[]) => AuthorizerResponse
22+
export type BouncerAuthorizer<User extends Record<string, any>> = (user: User, ...args: any[]) => AuthorizerResponse
2323

2424
/**
2525
* Represents an ability that can be used by a bouncer.
2626
*/
27-
export type BouncerAbility<User> = {
27+
export type BouncerAbility<User extends Record<string, any>> = {
2828
original: BouncerAuthorizer<User>
2929
execute: (user: User | null, ...args: any[]) => AuthorizerResponse
3030
allowGuest: boolean
3131
}
32+
33+
/**
34+
* Represents the arguments that will be passed to the authorizer function.
35+
*/
36+
export type BouncerArgs<Ability extends BouncerAbility<any>> = Ability extends { original: (user: any, ...args: infer Args) => AuthorizerResponse } ? Args : never

0 commit comments

Comments
 (0)