UNPKG

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
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 };