import { z } from "zod"; import { unkeyPermissionValidation } from "./permissions"; type Rule = "and" | "or"; export type PermissionQuery = | R | { and: PermissionQuery[]; or?: never; } | { and?: never; or: PermissionQuery[]; }; export const permissionQuerySchema: z.ZodType = z.union([ z.string(), z.object({ and: z.array(z.lazy(() => permissionQuerySchema)).min(1, "provide at least one permission"), }), z.object({ or: z.array(z.lazy(() => permissionQuerySchema)).min(1, "provide at least one permission"), }), ]); function merge(rule: Rule, ...args: PermissionQuery[]): PermissionQuery { return args.reduce( (acc: PermissionQuery, arg) => { if (typeof acc === "string") { throw new Error("Cannot merge into a string"); } if (!acc[rule]) { acc[rule] = []; } acc[rule]!.push(arg); return acc; }, {} as PermissionQuery, ); } export function or(...args: PermissionQuery[]): PermissionQuery { return merge("or", ...args); } export function and(...args: PermissionQuery[]): PermissionQuery { return merge("and", ...args); } export function buildQuery( fn: (ops: { or: typeof or; and: typeof and }) => PermissionQuery, ): PermissionQuery { return fn({ or, and }); } /** * buildUnkeyQuery is preloaded with out available roles and ensures typesafety for root key validation */ export const buildUnkeyQuery = buildQuery>;