UNPKG

permix

Version:

Permix is a lightweight, framework-agnostic, type-safe permissions management library for JavaScript applications on the client and server sides.

109 lines (108 loc) 2.89 kB
import { PermixNotFoundError, createCheckContext, createHooks, createPermix as createPermix$1, createTemplate } from "../core/index.mjs"; import fp from "fastify-plugin"; //#region src/fastify/permix.ts let pluginCounter = 0; function buildPermix(resolveKey, options = {}) { const onForbidden = options.onForbidden ?? (({ reply }) => { reply.status(403).send({ error: "Forbidden" }); }); const pluginName = `permix-${pluginCounter++}`; const hooks = createHooks(); function get(request) { try { return request.getDecorator(resolveKey()) ?? null; } catch { return null; } } function getOrThrow(request) { const instance = get(request); if (!instance) throw new PermixNotFoundError(resolveKey()); return instance; } function setupMiddleware(callbackOrRules) { return fp(async (fastify) => { fastify.decorateRequest(resolveKey(), null); fastify.addHook("onRequest", async (request, reply) => { const instance = createPermix$1(typeof callbackOrRules === "function" ? await callbackOrRules({ request, reply }) : callbackOrRules); instance.hook("check", (context) => hooks.callHook("check", context)); request.setDecorator(resolveKey(), instance); }); }, { fastify: "5.x", name: pluginName }); } const checkMiddleware = (...args) => { return async (request, reply) => { const permix = get(request); if (!permix) throw new PermixNotFoundError(resolveKey()); if (!permix.check(...args)) await onForbidden({ request, reply, ...createCheckContext(...args) }); }; }; function getRules(request) { return get(request)?.getRules() ?? null; } function template(rules) { return createTemplate(rules); } return { setupMiddleware, checkMiddleware, template, get, getOrThrow, getRules, hook: hooks.hook, hookOnce: hooks.hookOnce, get key() { return resolveKey(); }, $inferDefinition: void 0, $inferPath: void 0 }; } /** * Create a plugin factory that wires Permix into Fastify routes. * * Use `.contextKey('name')` to set a custom request decorator key (defaults to * a unique `Symbol('permix')`). * * @example * ```ts * import Fastify from 'fastify' * import { createPermix } from 'permix/fastify' * * const permix = createPermix<{ * post: ['create', 'read'] * }>() * * const app = Fastify() * await app.register(permix.setupMiddleware(({ request }) => ({ * post: { create: !!request.user, read: true }, * }))) * * app.get('/posts', { * preHandler: permix.checkMiddleware('post.read'), * }, (request, reply) => reply.send({ ok: true })) * ``` * * @link https://permix.letstri.dev/docs/integrations/fastify */ function createPermix(options = {}) { let key = Symbol("permix"); const permix = buildPermix(() => key, options); return Object.assign(permix, { contextKey(newKey) { key = newKey; return permix; } }); } //#endregion export { createPermix };