permix
Version:
Permix is a lightweight, framework-agnostic, type-safe permissions management library for JavaScript applications on the client and server sides.
268 lines (264 loc) • 8.84 kB
TypeScript
type CheckFunctionParams<Definition extends PermixDefinition, K extends keyof Definition> = Definition[K]['dataRequired'] extends true ? [
entity: K,
action: 'all' | Definition[K]['action'] | Definition[K]['action'][],
data: Definition[K]['dataType']
] : [
entity: K,
action: 'all' | Definition[K]['action'] | Definition[K]['action'][],
data?: Definition[K]['dataType']
];
type CheckFunctionObject<Definition extends PermixDefinition, K extends keyof Definition> = Definition[K]['dataRequired'] extends true ? {
entity: K;
action: 'all' | Definition[K]['action'] | Definition[K]['action'][];
data: Definition[K]['dataType'];
} : {
entity: K;
action: 'all' | Definition[K]['action'] | Definition[K]['action'][];
data?: Definition[K]['dataType'];
};
interface CheckContext<Definition extends PermixDefinition> {
entity: keyof Definition;
actions: Definition[keyof Definition]['action'][];
}
declare function createTemplate<T, Definition extends PermixDefinition>(rules: PermixRules<Definition> | ((param: T) => PermixRules<Definition>)): (param: T) => PermixRules<Definition>;
declare function createHooks<Definition extends PermixDefinition>(): {
hook: <K extends "setup" | "ready" | "hydrate">(name: K, fn: {
setup: (state: PermixRules<Definition>) => void;
ready: () => void;
hydrate: () => void;
}[K]) => () => void;
hookOnce: <K extends "setup" | "ready" | "hydrate">(name: K, fn: {
setup: (state: PermixRules<Definition>) => void;
ready: () => void;
hydrate: () => void;
}[K]) => void;
removeHook: <K extends "setup" | "ready" | "hydrate">(name: K, fn: {
setup: (state: PermixRules<Definition>) => void;
ready: () => void;
hydrate: () => void;
}[K]) => void;
callHook: <K extends "setup" | "ready" | "hydrate">(name: K, ...args: Parameters<{
setup: (state: PermixRules<Definition>) => void;
ready: () => void;
hydrate: () => void;
}[K]>) => void;
clearHook: <K extends "setup" | "ready" | "hydrate">(name: K) => void;
clearAllHooks: () => void;
};
type PermixDefinition<T extends Record<string, {
action: string;
dataType?: unknown;
dataRequired?: boolean;
}> = Record<string, {
action: string;
dataType?: unknown;
dataRequired?: boolean;
}>> = T;
type PermixStateJSON<Definition extends PermixDefinition = PermixDefinition> = {
[Key in keyof Definition]: {
[Action in Definition[Key]['action']]: boolean;
};
};
type PermixRules<Definition extends PermixDefinition = PermixDefinition> = {
[Key in keyof Definition]: {
[Action in Definition[Key]['action']]: boolean | (Definition[Key]['dataRequired'] extends true ? ((data: Definition[Key]['dataType']) => boolean) : ((data: Definition[Key]['dataType'] | undefined) => boolean));
};
};
declare function checkWithRules<Definition extends PermixDefinition, K extends keyof Definition>(state: PermixRules<Definition> | null, ...[entity, action, data]: CheckFunctionParams<Definition, K>): boolean;
/**
* Interface for the Permix permission manager
* @example
* ```ts
* const permix = createPermix<{
* post: {
* dataType: { id: string }
* action: 'create' | 'read'
* }
* }>()
* ```
*/
interface Permix<Definition extends PermixDefinition> {
/**
* Check if an action is allowed for an entity using current permissions.
*
* @link https://permix.letstri.dev/docs/guide/check
*
* @example
* ```ts
* // Single action check
* permix.check('post', 'create') // returns true if allowed
*
* // Multiple actions check
* permix.check('post', ['create', 'read']) // returns true if both actions are allowed
*
* // With data
* permix.check('post', 'read', { id: '123' }) // returns true if allowed exactly with this post
*
* // All actions check
* permix.check('post', 'all') // returns true if ALL actions are allowed
* ```
*/
check: <K extends keyof Definition>(...args: CheckFunctionParams<Definition, K>) => boolean;
/**
* Similar to `check`, but returns a Promise that resolves once `setup` is called.
* This ensures permissions are ready before checking them.
*
* @link https://permix.letstri.dev/docs/guide/check
*
* @example
* ```ts
* // Wait for permissions to be ready
* const canCreate = await permix.checkAsync('post', 'create') // Promise<true>
*
* // Multiple actions
* const canCreateAndRead = await permix.checkAsync('post', ['create', 'read'])
*
* // Even if you call setup after checking
* permix.setup({ post: { create: true } })
* const canCreate = await permix.checkAsync('post', 'create') // Promise<true>
* ```
*/
checkAsync: <K extends keyof Definition>(...args: CheckFunctionParams<Definition, K>) => Promise<boolean>;
/**
* Set up permissions.
*
* @link https://permix.letstri.dev/docs/guide/setup
*
* @example
* ```ts
* // Direct permissions object
* permix.setup({
* post: { create: true, read: false }
* })
* ```
*/
setup: <Rules extends PermixRules<Definition>>(callback: Rules) => void;
/**
* Register event handler.
*
* @link https://permix.letstri.dev/docs/guide/events
*
* @returns Function to remove the hook
*
* @example
* ```ts
* permix.on('setup', () => {
* console.log('Permissions were updated')
* })
* ```
*/
hook: ReturnType<typeof createHooks<Definition>>['hook'];
/**
* Similar to `hook`, but will be called only once.
*
* @link https://permix.letstri.dev/docs/guide/events
*
* @returns Function to remove the hook
*
* @example
* ```ts
* permix.hookOnce('setup', () => {
* console.log('Permissions were updated')
* })
* ```
*/
hookOnce: ReturnType<typeof createHooks<Definition>>['hookOnce'];
/**
* Define permissions in different place to setup them later.
*
* @link https://permix.letstri.dev/docs/guide/template
*
* @example
* ```ts
* // Some file where you want to define setup without permix instance
* import { permix } from './permix'
*
* const adminPermissions = permix.template({
* post: {
* create: true,
* read: false
* }
* })
*
* // Now you can use setup
* permix.setup(adminPermissions)
* ```
*/
template: <T = void>(...params: Parameters<typeof createTemplate<T, Definition>>) => ReturnType<typeof createTemplate<T, Definition>>;
/**
* Check if the setup was called.
*
* @link https://permix.letstri.dev/docs/guide/ready
*
* @example
* ```ts
* const isReady = permix.isReady()
* ```
*/
isReady: () => boolean;
/**
* Similar to `isReady`, but returns a Promise that resolves once `setup` is called.
*
* @link https://permix.letstri.dev/docs/guide/ready
*
* @example
* ```ts
* const isReady = await permix.isReadyAsync()
* ```
*/
isReadyAsync: () => Promise<boolean>;
/**
* Dehydrate the Permix instance.
*
* @link https://permix.letstri.dev/docs/guide/hydration
*
* @example
* ```ts
* const state = permix.dehydrate()
* ```
*/
dehydrate: () => PermixStateJSON<Definition>;
/**
* Hydrate the Permix instance.
*
* @link https://permix.letstri.dev/docs/guide/hydration
*
* @example
* ```ts
* const state = permix.dehydrate()
* permix.hydrate(state)
* ```
*/
hydrate: (state: PermixStateJSON<Definition>) => void;
}
/**
* Create a Permix instance
*
* @link https://permix.letstri.dev/docs/guide/instance
*
* @example
* ```ts
* const permix = createPermix<{
* post: {
* dataType: { id: string }
* action: 'create' | 'read'
* },
* user: {
* dataType: { id: string }
* action: 'create' | 'read'
* }
* }>()
*
* permix.setup({
* post: { create: false },
* user: { read: true }
* })
*
* console.log(permix.check('post', 'create')) // false
* console.log(permix.check('user', 'read')) // true
* ```
*/
declare function createPermix<Definition extends PermixDefinition>(initial?: PermixRules<Definition>): Permix<Definition>;
declare function getRules<Definition extends PermixDefinition>(permix: Permix<Definition>): PermixRules<Definition>;
export { checkWithRules as c, createPermix as d, getRules as g };
export type { CheckContext as C, PermixDefinition as P, Permix as a, PermixStateJSON as b, PermixRules as e, CheckFunctionObject as f, CheckFunctionParams as h };