### Install Dependencies and Run Demo Source: https://github.com/letstri/permix/blob/main/examples/tanstack-start/README.md Instructions to install project dependencies, navigate to the TanStack Start example directory, and start the development server. ```bash pnpm install cd examples/tanstack-start pnpm dev ``` -------------------------------- ### Quick Start with Permix Source: https://github.com/letstri/permix/blob/main/README.md Initialize Permix, set up permissions, and perform a check. Ensure 'permix' is installed as a dependency. ```typescript import { createPermix } from 'permix' const permix = createPermix<{ post: ['read'] }>() permix.setup({ post: { read: true, } }) permix.check('post.read') // true ``` -------------------------------- ### Permix Setup with Templates Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/next.mdx Define reusable rule sets using `template()` and configure Permix globally in `app/layout.tsx` based on user roles. This example shows creating admin and guest templates. ```ts import { createPermix } from 'permix/next' export const permix = createPermix<{ post: ['create', 'read', 'update', 'delete'] }>() export const adminTemplate = permix.template({ post: { create: true, read: true, update: true, delete: true }, }) export const guestTemplate = permix.template({ post: { create: false, read: true, update: false, delete: false }, }) ``` ```tsx import { permix, adminTemplate, guestTemplate } from '@/lib/permix' import { getSession } from '@/lib/auth' const session = await getSession() permix.setup(session?.role === 'admin' ? adminTemplate() : guestTemplate()) ``` -------------------------------- ### Define Permix Templates with TanStack Start Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/tanstack-start.mdx Use `createPermix` from `permix/tanstack-start` to define your Permix instance and create reusable rule templates. This example defines templates for administrators and guests, specifying permissions for 'post' resources. ```ts import { createPermix } from 'permix/tanstack-start' export const permix = createPermix<{ post: ['create', 'read', 'update', 'delete'] }>() export const adminTemplate = permix.template({ post: { create: true, read: true, update: true, delete: true }, }) export const guestTemplate = permix.template({ post: { create: false, read: true, update: false, delete: false }, }) ``` -------------------------------- ### Registering Event Handlers with hook and hookOnce Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/events.mdx Use `hook` to register a handler that is called every time an event is triggered, and `hookOnce` for handlers that should only be called once. This example demonstrates setting up event listeners for the 'setup' event. ```ts const permix = createPermix<{ post: ['create', 'read'] }>() // The handler will be called every time setup is executed permix.hook('setup', () => { console.log('Permissions were updated') }) // The handler will be called only once permix.hookOnce('setup', () => { console.log('Permissions were updated once') }) // Calling `setup` triggers the `setup` event // and `ready` on the first successful setup permix.setup({ post: { create: true, read: true } }) ``` -------------------------------- ### Create Permix Instance and Setup Separately Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/instance.mdx This demonstrates the equivalent setup where permissions are configured after the Permix instance is created. This approach is useful if permissions are not known at initialization time. ```typescript import { createPermix } from 'permix' const permix = createPermix<{ post: ['create', 'edit'] }>() permix.setup({ post: { create: true, edit: false } }) ``` -------------------------------- ### Hook into Setup Event Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-getting-started/SKILL.md Subscribe to the 'setup' event to re-run logic, such as refreshing UI caches, whenever the permission rules are updated. ```typescript permix.hook('setup', () => { // re-run when rules change (e.g. refresh UI cache) }) ``` -------------------------------- ### Run Development Server Source: https://github.com/letstri/permix/blob/main/docs/README.md Use this command to start the development server. Open http://localhost:3000 with your browser to see the result. ```bash pnpm dev ``` -------------------------------- ### tRPC/oRPC Context Setup and Middleware Changes Source: https://github.com/letstri/permix/blob/main/docs/content/docs/migration-v3-to-v4.mdx Illustrates the shift from v3's `createPermix` and `setup` to v4's `createPermix` with `.contextKey()` and `setupContext`. It also shows the updated dot-path syntax for middleware checks. ```typescript import { createPermix } from 'permix/trpc' const permix = createPermix<{ post: ['create', 'read', 'update', 'delete'] }>().contextKey('permissions') // optional; default key is 'permix' protectedProcedure.use(({ ctx, next }) => { return next({ ctx: permix.setupContext({ post: { ... } }), }) }) createPost.use(permix.checkMiddleware('post.create')) updatePost.use(permix.checkMiddleware(c => c('post.read') && c('post.update'))) adminAction.use(permix.checkMiddleware('post.~all')) ``` -------------------------------- ### Setup Permix Middleware for oRPC Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/orpc.mdx Integrate Permix into your oRPC server by creating a middleware. This example shows how to set up the Permix instance with a custom context key and define a protected middleware that checks user roles for access. ```typescript import { os } from '@orpc/server' import { createPermix } from 'permix/orpc' interface Post { id: string title: string } interface Context { user: { id: string role: string } } const orpcPermix = os.$context() // Create your Permix instance with a custom context key const permix = createPermix<{ post: ['create', 'read', 'update'] user: ['delete'] }>().contextKey('permissions') // Create a protected middleware with Permix const protectedMiddleware = orpcPermix.use(({ context, next }) => { const isAdmin = context.user.role === 'admin' return next({ context: permix.setupContext({ post: { create: true, read: true, update: isAdmin }, user: { delete: isAdmin } }) }) }) ``` -------------------------------- ### Express/Hono/Fastify/Elysia/Node Middleware Setup Source: https://github.com/letstri/permix/blob/main/docs/content/docs/migration-v3-to-v4.mdx Demonstrates how to set up middleware in v4 for Node.js server frameworks, showing both direct rule passing and callback-based rule definition. ```typescript app.use(permix.setupMiddleware({ post: { read: true } })) // or app.use(permix.setupMiddleware(async ({ req }) => ({ post: { read: req.user.isAdmin } }))) ``` -------------------------------- ### Basic Permix Setup Source: https://github.com/letstri/permix/blob/main/docs/content/docs/index.mdx Initialize Permix with a simple permission definition and set up basic read access. ```typescript import { createPermix } from 'permix' const permix = createPermix<{ post: ['read'] }>() permix.setup({ post: { read: true, } }) const canReadPost = permix.check('post.read') // true ``` -------------------------------- ### ReBAC Rule Closure Example Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/rebac.mdx Illustrates how rule functions capture the actor at setup time and receive the resource at check time to enforce relationship-based permissions. ```typescript permix.setup({ doc: { update: (doc) => doc.authorId === currentUser.id, }, }) ``` -------------------------------- ### Define Object Permissions with Setup Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/setup.mdx Use the `setup` method to define object-based permissions after creating a Permix instance. This method replaces any previously defined permissions. ```typescript const permix = createPermix<{ post: ['create'] comment: ['create', 'update'] }>() permix.setup({ post: { create: true, }, comment: { create: true, update: true, } }) ``` -------------------------------- ### Setup Permix in Next.js Root Layout Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/next.mdx Call `permix.setup()` early in the request lifecycle, typically in the root layout. Resolve any async data first, then pass plain rules to `setup`. This setup is scoped to the current request. ```tsx import { permix } from '@/lib/permix' import { getSession } from '@/lib/auth' export default async function RootLayout({ children, }: { children: React.ReactNode }) { const session = await getSession() permix.setup({ post: { create: !!session, read: true, update: post => post?.authorId === session?.userId, delete: session?.role === 'admin', }, }) return ( {children} ) } ``` -------------------------------- ### SSR: Hydrate and Setup Permix Client Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/ready.mdx Demonstrates the SSR flow where a server instance dehydrates permissions, and a client instance hydrates them. The client must call `setup()` again to restore function-based rules and make the instance ready. ```typescript // Server instance import { createPermix } from 'permix' const serverPermix = createPermix<{ post: [{ name: 'read', type: { isPublic: boolean } }] }>() serverPermix.setup({ post: { read: post => !!post?.isPublic, }, }) const state = serverPermix.dehydrate() // { post: { read: false } } — functions evaluated without data // Client instance (separate from the server) const clientPermix = createPermix<{ post: [{ name: 'read', type: { isPublic: boolean } }] }>() clientPermix.hydrate(state) console.log(clientPermix.isReady()) // false // Boolean rules from hydration work immediately: console.log(clientPermix.check('post.read')) // false // Restore function-based rules and mark the instance ready clientPermix.setup({ post: { read: post => !!post?.isPublic, }, }) console.log(clientPermix.isReady()) // true console.log(clientPermix.check('post.read', { isPublic: true })) // true ``` -------------------------------- ### Install Permix and Effect Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/effect.mdx Install the necessary packages for Permix and Effect integration using npm. ```bash npm install permix effect ``` -------------------------------- ### Install Agent Skills with TanStack Intent Source: https://github.com/letstri/permix/blob/main/README.md Install Permix and then use pnpm dlx to install agent skills via TanStack Intent. Skills are versioned and indexed on the Agent Skills Registry. ```bash pnpm dlx @tanstack/intent@latest install ``` -------------------------------- ### Install Permix Package Source: https://github.com/letstri/permix/blob/main/docs/content/docs/quick-start.mdx Install Permix using your preferred package manager. ```bash permix ``` -------------------------------- ### Drizzle v1 Schema and Permix Setup Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/drizzle.mdx Define your Drizzle schema with relations and then create a Permix instance. Configure specific CRUD permissions for each table. This example uses Drizzle v1 syntax. ```typescript import { defineRelations } from 'drizzle-orm' import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core' import { createPermix } from 'permix/drizzle' const users = pgTable('users', { id: serial('id').primaryKey(), name: text('name').notNull(), }) const posts = pgTable('posts', { id: serial('id').primaryKey(), title: text('title').notNull(), authorId: integer('author_id').notNull().references(() => users.id), }) const relations = defineRelations({ users, posts }, r => ({ posts: { author: r.one.users({ from: r.posts.authorId, to: r.users.id }) }, })) const schema = { users, posts, relations } const permix = createPermix(schema) permix.setup({ users: { create: true, read: true, update: false, delete: false }, posts: { create: true, read: true, update: true, delete: false }, }) permix.check('users.read') // true permix.check('posts.delete') // false ``` -------------------------------- ### Dynamically Fetch and Setup Permissions Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/setup.mdx Use async functions to fetch permissions from external sources and then set them up. This is useful when permissions need to be loaded at runtime. ```typescript import { createPermix } from 'permix' const permix = createPermix<{ post: ['create', 'read', 'update', 'delete'] comment: ['create', 'read', 'update', 'delete'] }>() // Fetch permissions from API async function loadUserPermissions(userId: string) { const permissions = await getPermissionsFromAnyPlace() permix.setup({ post: { create: permissions.includes('post:create'), read: permissions.includes('post:read'), update: permissions.includes('post:update'), delete: permissions.includes('post:delete'), }, comment: { create: permissions.includes('comment:create'), read: permissions.includes('comment:read'), update: permissions.includes('comment:update'), delete: permissions.includes('comment:delete'), }, }) } // Usage await loadUserPermissions('user-123') ``` -------------------------------- ### Async Permission Rules Setup with Hono Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/hono.mdx Use async functions to fetch user permissions from a database during middleware setup. This allows for dynamic permission configurations based on external data. ```typescript app.use(permix.setupMiddleware(async ({ c }) => { // Fetch user permissions from database const user = c.get('user') const userPermissions = await getUserPermissions(user.id) return { post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts, delete: userPermissions.canDeletePosts } } })) ``` -------------------------------- ### Manual Skill Installation for Cursor Source: https://github.com/letstri/permix/blob/main/permix/skills/README.md Copy Permix skill folders directly into the `.cursor/skills/` directory for manual integration with the Cursor editor. ```bash cp -r node_modules/permix/skills/permix-* .cursor/skills/ ``` -------------------------------- ### Drizzle v0 Schema and Permix Setup Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/drizzle.mdx Define your Drizzle schema and create a Permix instance using the legacy integration path. Configure specific CRUD permissions for each table. This example uses Drizzle v0 syntax. ```typescript import { relations } from 'drizzle-orm/_relations' import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core' import { createPermix } from 'permix/drizzle/legacy' const users = pgTable('users', { id: serial('id').primaryKey(), name: text('name').notNull(), }) const posts = pgTable('posts', { id: serial('id').primaryKey(), title: text('title').notNull(), authorId: integer('author_id').notNull().references(() => users.id), }) const usersRelations = relations(users, ({ many }) => ({ posts: many(posts), })) const schema = { users, posts, usersRelations } const permix = createPermix(schema) permix.setup({ users: { create: true, read: true, update: false, delete: false }, posts: { create: true, read: true, update: true, delete: false }, }) permix.check('users.read') // true permix.check('posts.delete') // false ``` -------------------------------- ### Setup Permix Middleware in Hono Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/hono.mdx Initialize Hono and Permix, then set up the middleware with your permission rules. Access user data from the context to define granular permissions. ```typescript import { Hono } from 'hono' import { createPermix } from 'permix/hono' interface Post { id: string authorId: string title: string content: string } // Initialize Hono const app = new Hono() // Create your Permix instance const permix = createPermix<{ post: [ { name: 'create', type: Post }, { name: 'read', type: Post }, { name: 'update', type: Post }, { name: 'delete', type: Post }, ] }>() // Set up the middleware with your permission rules app.use(permix.setupMiddleware(({ c }) => { // You can access c.get('user') or other properties to determine permissions const user = c.get('user') const isAdmin = user?.role === 'admin' return { post: { create: true, read: true, update: isAdmin, delete: isAdmin } } })) ``` -------------------------------- ### Check Before Setup Behavior Source: https://github.com/letstri/permix/blob/main/docs/content/docs/migration-v3-to-v4.mdx Highlights the change in behavior when `check()` is called before `setup()`. v3 logs and returns false, while v4 throws a `PermixNotReadyError`. ```typescript check() before setup(): logs + returns false ``` ```typescript check() before rules exist: throws PermixNotReadyError ``` -------------------------------- ### Hook into Ready Event Once Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-getting-started/SKILL.md Subscribe to the 'ready' event to execute logic only once after the first successful setup of permissions. ```typescript permix.hookOnce('ready', () => { // first successful setup only }) ``` -------------------------------- ### Install Permix Skills via npm Source: https://github.com/letstri/permix/blob/main/permix/skills/README.md Install the Permix package and use TanStack Intent to discover and load skills. Intent automatically configures your agent with skill-loading guidance. ```bash pnpm add permix pnpm dlx @tanstack/intent@latest install ``` -------------------------------- ### Wire up Permix in Root Layout Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/next.mdx Integrate Permix setup into your Next.js root layout. This involves getting the session, setting up Permix with rules, and passing the dehydrated state to the client providers. Function-based rules are lost during serialization and may require client-side re-setup if `isReady` is needed. ```tsx import { permix } from '@/lib/permix' import { getSession } from '@/lib/auth' import { Providers } from './providers' export default async function RootLayout({ children, }: { children: React.ReactNode }) { const session = await getSession() permix.setup({ /* ...rules derived from session... */ }) return ( {children} ) } ``` -------------------------------- ### Setup Permix After Authentication Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-frontend/SKILL.md Call permix.setup with role rules after the user has been authenticated and their session loaded. This initializes Permix with the necessary permission configurations. ```ts // e.g. after session loads await loadUser() permix.setup(roleRulesFor(user)) ``` -------------------------------- ### SSR Flow Diagram Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-ssr/SKILL.md This diagram illustrates the flow of permissions from server setup and dehydration to client hydration and readiness. ```text Server: setup(rules) → dehydrate() → send state Client: hydrate(state) → setup(fullRules) → isReady() → check() / usePermix ``` -------------------------------- ### Check Permix Ready State (Basic) Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/ready.mdx Use `isReady()` to synchronously check if Permix has been fully set up and is ready to process permission checks. This is useful after calling `setup()` to confirm initialization. ```typescript import { createPermix } from 'permix' const permix = createPermix<{ post: ['create', 'read'] }>() console.log(permix.isReady()) // false // After setup completes permix.setup({ post: { create: true, read: true } }) console.log(permix.isReady()) // true ``` -------------------------------- ### Setup Permix Middleware for Express Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/express.mdx Initialize Express, create a Permix instance with defined resource types and actions, and set up the middleware to determine user permissions based on request context. ```typescript import express from 'express' import { createPermix } from 'permix/express' interface Post { id: string authorId: string title: string content: string } // Initialize Express const app = express() // Create your Permix instance const permix = createPermix<{ post: [ { name: 'create', type: Post }, { name: 'read', type: Post }, { name: 'update', type: Post }, ] }>() // Set up the middleware with your permission rules app.use(permix.setupMiddleware(({ req }) => { // You can access req.user or other properties to determine permissions return { post: { create: true, read: true, update: false } } })) ``` -------------------------------- ### Perform First Permission Check Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-getting-started/SKILL.md Perform a permission check for a specific action on a resource. Before `setup` is called, this will throw a `PermixNotReadyError`. Unknown paths will throw a `PermixRuleNotDefinedError`. ```typescript permix.check('post.read') // boolean ``` -------------------------------- ### Setup Permix Plugin with Fastify Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/fastify.mdx Initialize Fastify and register the Permix middleware. The middleware function receives request and reply objects and should return an object defining permissions for the current context. ```typescript import Fastify from 'fastify' import { createPermix } from 'permix/fastify' interface Post { id: string authorId: string title: string content: string } // Initialize Fastify const fastify = Fastify() // Create your Permix instance const permix = createPermix<{ post: [ { name: 'create', type: Post }, { name: 'read', type: Post }, { name: 'update', type: Post }, ] }>() // Set up the plugin with your permission rules await fastify.register(permix.setupMiddleware(({ request, reply }) => { // You can access request.user or other properties to determine permissions return { post: { create: true, read: true, update: false } } })) ``` -------------------------------- ### Assign Rules with Setup Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-getting-started/SKILL.md Assign specific rules for each action within the defined permission schema. This function replaces any previously set rules, so it should be called after login or when the active user/tenant changes. ```typescript permix.setup({ post: { create: true, read: true, update: false, delete: false, }, comment: { create: true, read: true, }, }) ``` -------------------------------- ### Advanced Permix Setup with Type Definitions Source: https://github.com/letstri/permix/blob/main/docs/content/docs/index.mdx Define complex permissions using TypeScript interfaces for users and resources, and set up role-based access control. ```typescript import { createPermix } from 'permix' // You can take types from your database interface User { id: string role: 'editor' | 'user' } interface Post { id: string title: string authorId: string published: boolean } interface Comment { id: string content: string authorId: string } // Create definition to describe your permissions type PermissionsDefinition = { post: [ { name: 'create', type: Post }, { name: 'read', type: Post }, { name: 'update', type: Post }, { name: 'delete', type: Post }, ] comment: [ { name: 'create', type: Comment }, { name: 'read', type: Comment }, { name: 'update', type: Comment }, ] } const permix = createPermix() // Define permissions for different users const editorPermissions = permix.template({ post: { create: true, read: true, update: post => !post?.published, delete: post => !post?.published, }, comment: { create: false, read: true, update: false, }, }) const userPermissions = permix.template(({ id: userId }: User) => ({ post: { create: false, read: true, update: false, delete: false, }, comment: { create: true, read: true, update: comment => comment?.authorId === userId, }, })) async function getUser() { // Imagine that this function is fetching user from database return { id: '1', role: 'editor' as const, } } // Setup permissions for signed in user async function setupPermix() { const user = await getUser() const permissionsMap = { editor: () => editorPermissions(), user: () => userPermissions(user), } permix.setup(permissionsMap[user.role]()) } // Call setupPermix where you need to setup permissions setupPermix() // Check if a user has permission to do something const canCreatePost = permix.check('post.create') async function getComment() { // Imagine that this function is fetching comment from database return { id: '1', content: 'Hello, world!', authorId: '1', } } const comment = await getComment() const canUpdateComment = permix.check('comment.update', comment) ``` -------------------------------- ### React Provider Setup Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-frontend/SKILL.md Wrap your application with PermixProvider to make Permix available to all child components. Ensure the same permix instance is passed to both the provider and any hooks. ```tsx import { PermixProvider } from 'permix/react' import { permix } from './lib/permix' export function App() { return ( ) } ``` -------------------------------- ### Async Permission Rules Setup Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/server.mdx Configure Permix middleware with asynchronous permission rules. This is useful for fetching user-specific permissions from a database or external service during the request lifecycle. ```typescript serve({ middleware: [ permix.setupMiddleware(async ({ req }) => { // Fetch user permissions from database const userId = req.headers.get('x-user-id') const userPermissions = await getUserPermissions(userId) return { post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts, delete: userPermissions.canDeletePosts, }, } }), ], fetch(req) { return Response.json({ ok: true }) }, }) ``` -------------------------------- ### Setup PermixProvider in App.tsx Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/solid.mdx Wrap your Solid application with PermixProvider to make Permix available throughout your app. Ensure you pass the Permix instance to the provider. ```tsx import { PermixProvider } from 'permix/solid' import { permix } from './lib/permix' function App() { return ( ) } ``` -------------------------------- ### App Setup with PermixProvider Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/react.mdx Wrap your React application with PermixProvider to make Permix available throughout your app. Ensure you pass the Permix instance to the provider. ```tsx import { PermixProvider } from 'permix/react' import { permix } from './lib/permix' function App() { return ( ) } ``` -------------------------------- ### Create Reusable Role Presets Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-getting-started/SKILL.md Define reusable role presets using `template` to encapsulate common rule sets. These templates can be applied later using `setup`. ```typescript const admin = permix.template({ post: { create: true, read: true, update: true, delete: true }, }) const member = permix.template({ post: { create: false, read: true, update: true, delete: false }, }) // After resolving the user's role: permix.setup(admin()) ``` -------------------------------- ### Vue Provider Setup Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-frontend/SKILL.md Set up the PermixProvider in your Vue application to manage permissions. This component is imported from 'permix/vue' and requires the permix instance to be passed as a prop. ```vue ``` -------------------------------- ### Setup PermixProvider in App.vue Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/vue.mdx Wrap your Vue application with PermixProvider to make Permix available throughout your app. Ensure you pass the Permix instance to the provider. ```vue ``` -------------------------------- ### SSR Hydration Setup with PermixHydrate Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/svelte.mdx For Server-Side Rendering (SSR), wrap your application with PermixHydrate and provide the dehydrated server state. Call permix.setup() on the client after mounting to restore function-based rules and set the 'isReady' state. ```svelte {@render children()} ``` -------------------------------- ### Setup Permix Permissions Source: https://github.com/letstri/permix/blob/main/docs/src/routes/-code/setup.mdx Configure permissions for 'post' actions. 'create' is always allowed. 'edit' depends on the post's published status or if the user is an admin. 'delete' is restricted to admins. ```typescript import { permix } from './permix' // Fetch the user const user = await fetchUser() // Setup the permissions permix.setup({ post: { create: true, edit: post => post ? !post.published : user.role === 'admin', delete: user.role === 'admin', }, }) ``` -------------------------------- ### Setup User Permissions with Document Sharing Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/rebac.mdx Configure Permix to manage document access based on ownership, team membership, and explicit sharing with different levels (read, write, admin). ```typescript import { createPermix } from 'permix' interface Doc { id: string authorId: string teamId: string } type ShareLevel = 'read' | 'write' | 'admin' // In a real app this would come from your database const shares = new Map>() function hasShare(docId: string, userId: string, minLevel: ShareLevel): boolean { const level = shares.get(docId)?.get(userId) if (!level) return false const rank: Record = { read: 0, write: 1, admin: 2 } return rank[level] >= rank[minLevel] } const permix = createPermix<{ doc: [ { name: 'read', type: Doc, required: true }, { name: 'update', type: Doc, required: true }, { name: 'admin', type: Doc, required: true }, ] }>() interface User { id: string teamIds: string[] } function setupForUser(me: User) { const isOwner = (doc: Doc) => doc.authorId === me.id const isTeammate = (doc: Doc) => me.teamIds.includes(doc.teamId) permix.setup({ doc: { read: (doc) => isOwner(doc) || isTeammate(doc) || hasShare(doc.id, me.id, 'read'), update: (doc) => isOwner(doc) || hasShare(doc.id, me.id, 'write'), admin: (doc) => isOwner(doc) || hasShare(doc.id, me.id, 'admin'), }, }) } ``` -------------------------------- ### Checking Permix Readiness Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-check/SKILL.md Check if Permix has completed its setup or initial rule loading. Use `isReady()` for synchronous checks and `isReadyAsync()` to wait for asynchronous completion, useful for gating UI or early checks. ```typescript permix.isReady() // sync await permix.isReadyAsync() // wait until setup (or initial rules) completed ``` -------------------------------- ### Get Current Permix Rules Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/setup.mdx Use the `getRules` method to retrieve the exact rules object previously set with `setup`. This includes any custom permission functions. ```typescript // Get current rules const rules = permix.getRules() ``` -------------------------------- ### Setup Permix Middleware in Elysia Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/elysia.mdx Initialize Permix and set up its middleware in Elysia. The middleware allows you to define permissions based on request context, such as headers. It preserves type safety from your Permix definition. ```typescript import { Elysia } from 'elysia' import { createPermix } from 'permix/elysia' interface Post { id: string authorId: string title: string content: string } // Create your Permix instance const permix = createPermix<{ post: [ { name: 'create', type: Post }, { name: 'read', type: Post }, { name: 'update', type: Post }, { name: 'delete', type: Post }, ] }>() // Initialize Elysia const app = new Elysia() .onBeforeHandle(permix.setupMiddleware(({ context }) => { // You can access headers or other properties to determine permissions const isAuthorized = !!context.headers.authorization?.slice(7) return { post: { create: true, read: true, update: isAuthorized, delete: isAuthorized } } })) ``` -------------------------------- ### Build and Preview Project Source: https://github.com/letstri/permix/blob/main/docs/README.md Commands to build the project for production and preview the production build. ```bash pnpm build pnpm preview ``` -------------------------------- ### Setup tRPC Middleware with Permix Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/trpc.mdx Initialize tRPC, create a Permix instance with a custom context key, and define a protected procedure that uses Permix to conditionally grant permissions based on user role. ```typescript import { initTRPC } from '@trpc/server' import { createPermix } from 'permix/trpc' interface Post { id: string authorId: string title: string content: string } interface Context { user: { id: string role: string } } // Initialize tRPC const t = initTRPC.context().create() // Create your Permix instance with a custom context key const permix = createPermix<{ post: ['create', 'read', 'update', 'delete'] }>().contextKey('permissions') // Create a protected procedure with Permix const protectedProcedure = t.procedure.use(({ ctx, next }) => { const isAdmin = ctx.user.role === 'admin' return next({ ctx: permix.setupContext({ post: { create: true, read: true, update: isAdmin, delete: isAdmin } }) }) }) ``` -------------------------------- ### Full Permix Effect Integration Example Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/effect.mdx Defines permissions, a user service, builds a Permix layer based on user roles, and uses it within an Effect program. Provides the necessary layers and runs the program. ```typescript import { Context, Effect, Layer } from 'effect' import { createPermix } from 'permix/effect' // 1. Define permissions const permix = createPermix<{ post: ['create', 'read', 'update'] user: ['delete'] }>() // 2. Define a service for the current user interface User { id: string; role: 'admin' | 'member' } class CurrentUser extends Context.Tag('CurrentUser')() {} // 3. Build a Layer that derives rules from CurrentUser const PermixLive = permix.layerSetup(Effect.gen(function* () { const user = yield* CurrentUser return { post: { create: user.role === 'admin', read: true, update: user.role === 'admin', }, user: { delete: user.role === 'admin' }, } })) // 4. Use in a program const program = Effect.gen(function* () { const canCreate = yield* permix.check('post.create') const canRead = yield* permix.check('post.read') return { canCreate, canRead } }) // 5. Provide the layers and run const CurrentUserLive = Layer.succeed(CurrentUser, { id: '1', role: 'admin' }) Effect.runPromise( program.pipe( Effect.provide(PermixLive), Effect.provide(CurrentUserLive), ), ).then(console.log) // { canCreate: true, canRead: true } ``` -------------------------------- ### Entity-Aware Actions with ReBAC/ABAC Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-check/SKILL.md Define and check permissions that require resource data (ReBAC/ABAC). Attach a `type` to actions needing resource data at check time. The `setup` captures the actor, and `check` passes the resource. ```typescript interface Post { id: string authorId: string } const permix = createPermix<{ post: [ 'read', { name: 'update', type: Post }, { name: 'delete', type: Post, required: true }, ] }>() const currentUser = { id: userId } permix.setup({ post: { read: true, update: post => post?.authorId === currentUser.id, delete: post => post.authorId === currentUser.id, }, }) permix.check('post.update', post) // optional data if not required: true permix.check('post.delete', post) // required: true — data required ``` -------------------------------- ### Attach Rules Per Request with setupMiddleware Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-server/SKILL.md Use setupMiddleware to dynamically generate authorization rules based on the incoming request. This function can be synchronous or asynchronous and accepts request, response, and next parameters. ```typescript app.use(permix.setupMiddleware(async ({ req }) => { const user = req.user return { post: { create: true, read: true, update: post => post.authorId === user.id, }, } })) ``` -------------------------------- ### Define Permissions with createPermix Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/tanstack-start.mdx Define your permissions once using `ValidateDefinition` and create a Permix instance with `createPermix`. This instance is designed to be used on the server within TanStack Start's request context, ensuring each request gets an isolated instance. ```typescript import type { ValidateDefinition } from 'permix' import { createPermix } from 'permix/tanstack-start' interface Post { id: string authorId: string } // Define your permissions once and reuse this type on the server and client. export type PermissionsDefinition = ValidateDefinition<{ post: [ { name: 'create', type: Post }, { name: 'read', type: Post }, { name: 'update', type: Post }, { name: 'delete', type: Post }, ] }> export const permix = createPermix() ``` -------------------------------- ### Setup Permix Middleware with srvx Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/server.mdx Demonstrates setting up Permix middleware within an srvx server. It shows how to create a Permix instance with a defined schema and configure the middleware to extract user roles from request headers for permission checks. ```typescript import { serve } from 'srvx' import { createPermix } from 'permix/server' interface Post { id: string authorId: string title: string content: string } // Create your Permix instance const permix = createPermix<{ post: [ { name: 'create', type: Post }, { name: 'read', type: Post }, { name: 'update', type: Post }, { name: 'delete', type: Post }, ] }>() // Set up the middleware with your permission rules serve({ middleware: [ permix.setupMiddleware(({ req }) => { // You can read headers, cookies, or any request data to determine permissions const isAdmin = req.headers.get('x-user-role') === 'admin' return { post: { create: true, read: true, update: isAdmin, delete: isAdmin, }, } }), ], fetch(req) { return Response.json({ ok: true }) }, }) ``` -------------------------------- ### Install Permix v4 Source: https://github.com/letstri/permix/blob/main/docs/content/docs/migration-v3-to-v4.mdx Install the latest version of Permix. Ensure your TypeScript version is compatible, preferably v6 or higher. ```bash permix@^4 ``` -------------------------------- ### Hook into Permix Setup Events Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/effect.mdx Register listeners for permission setup events using `permix.hook`. The hook yields a function that can be called to remove the listener. ```typescript const program = Effect.gen(function* () { const remove = yield* permix.hook('setup', () => { console.log('Permissions updated') }) // ...later remove() }) ``` -------------------------------- ### Use Server-Side Templates Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-server/SKILL.md Define authorization rules as templates on the server and apply them using setupMiddleware. This is useful for reusable or complex rule sets. ```typescript const rules = permix.template(adminRules)() app.use(permix.setupMiddleware(rules)) ``` -------------------------------- ### Setup User Permissions based on Team Membership Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/rebac.mdx Configure Permix to allow users to read documents owned by their team and update documents they authored. This setup is for a user context. ```typescript import { createPermix } from 'permix' interface Doc { id: string authorId: string teamId: string } interface User { id: string teamIds: string[] } const permix = createPermix<{ doc: [ { name: 'read', type: Doc, required: true }, { name: 'update', type: Doc, required: true }, ] }>() function setupForUser(me: User) { permix.setup({ doc: { read: (doc) => me.teamIds.includes(doc.teamId), update: (doc) => doc.authorId === me.id, }, }) } const alice: User = { id: 'alice', teamIds: ['team-a'] } setupForUser(alice) const teamDoc = { id: 'doc-1', authorId: 'bob', teamId: 'team-a' } const otherTeamDoc = { id: 'doc-2', authorId: 'charlie', teamId: 'team-b' } permix.check('doc.read', teamDoc) // true — same team permix.check('doc.read', otherTeamDoc) // false — different team permix.check('doc.update', teamDoc) // false — alice is not the author ``` -------------------------------- ### List or Load a Specific Skill with Intent Source: https://github.com/letstri/permix/blob/main/permix/skills/README.md Use the TanStack Intent CLI to list available skills or load a specific skill, such as `permix-getting-started`. ```bash pnpm dlx @tanstack/intent@latest list ``` ```bash pnpm dlx @tanstack/intent@latest load permix#permix-getting-started ``` -------------------------------- ### Async Permission Rules with Express Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/express.mdx Use async functions in your permission setup to fetch user permissions from a database before returning them. ```typescript app.use(permix.setupMiddleware(async ({ req }) => { // Fetch user permissions from database const userPermissions = await getUserPermissions(req.user.id) return { post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts } } })) ``` -------------------------------- ### Register Global Request Middleware Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/tanstack-start.mdx Register `setupMiddleware()` as a global request middleware in `src/start.ts` to create a fresh, request-scoped instance for every request. The callback receives the `request` object, allowing access to cookies, headers, or user data for permission checks. ```typescript import { createStart, } from '@tanstack/react-start' import { getSession } from './lib/auth' import { permix } from './lib/permix' export const startInstance = createStart(() => ({ requestMiddleware: [ permix.setupMiddleware(async ({ request }) => { const session = await getSession(request) return { post: { create: !!session, read: true, update: post => post?.authorId === session?.userId, delete: session?.role === 'admin', }, } }), ], })) ``` -------------------------------- ### Server-side Dehydration Source: https://github.com/letstri/permix/blob/main/permix/skills/permix-ssr/SKILL.md On the server, set up Permix with your rules and then call `dehydrate()` to get a JSON snapshot of permissions. This state can then be passed to the client. ```typescript permix.setup(serverRules) const state = permix.dehydrate() // { post: { create: true, read: false } } — functions evaluated once without data ``` -------------------------------- ### Async Permission Rules with Elysia Source: https://github.com/letstri/permix/blob/main/docs/content/docs/integrations/elysia.mdx Use async functions within `permix.setupMiddleware` to fetch user permissions asynchronously before handling requests. This is useful for database lookups. ```typescript app.onBeforeHandle(permix.setupMiddleware(async ({ context }) => { // Fetch user permissions from database const userId = context.headers.authorization?.slice(7) const userPermissions = await getUserPermissions(userId) return { post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts, delete: userPermissions.canDeletePosts } } })) ``` -------------------------------- ### Create Permix Instance with Initial Rules Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/instance.mdx Use this method to set up permissions directly when creating a Permix instance. Ensure you pass the generic type for proper validation, even with initial rules. ```typescript import { createPermix } from 'permix' const permix = createPermix<{ post: ['create', 'edit'] }>({ post: { create: true, edit: false } }) // Permissions are immediately available console.log(permix.check('post.create')) // true console.log(permix.isReady()) // true ``` -------------------------------- ### Create Permix Instance in JavaScript Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/instance.mdx Initialize a Permix instance using `createPermix` in plain JavaScript projects. ```javascript import { createPermix } from 'permix' const permix = createPermix() ``` -------------------------------- ### Define Static Permissions with Template Source: https://github.com/letstri/permix/blob/main/docs/content/docs/guide/template.mdx Use `permix.template` to define static permissions. Later, call the template function to get the permission object for `permix.setup`. ```typescript const adminPermissions = permix.template({ post: { create: true, read: true } }) // Later, use the template to setup permissions permix.setup(adminPermissions()) ```