60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
import { z } from "zod";
|
|
import { unkeyPermissionValidation } from "./permissions";
|
|
|
|
type Rule = "and" | "or";
|
|
|
|
export type PermissionQuery<R extends string = string> =
|
|
| R
|
|
| {
|
|
and: PermissionQuery<R>[];
|
|
or?: never;
|
|
}
|
|
| {
|
|
and?: never;
|
|
or: PermissionQuery<R>[];
|
|
};
|
|
|
|
export const permissionQuerySchema: z.ZodType<PermissionQuery> = 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<R extends string>(rule: Rule, ...args: PermissionQuery<R>[]): PermissionQuery<R> {
|
|
return args.reduce(
|
|
(acc: PermissionQuery<R>, 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<R>,
|
|
);
|
|
}
|
|
|
|
export function or<R extends string = string>(...args: PermissionQuery<R>[]): PermissionQuery<R> {
|
|
return merge("or", ...args);
|
|
}
|
|
|
|
export function and<R extends string = string>(...args: PermissionQuery<R>[]): PermissionQuery<R> {
|
|
return merge("and", ...args);
|
|
}
|
|
export function buildQuery<R extends string = string>(
|
|
fn: (ops: { or: typeof or<R>; and: typeof and<R> }) => PermissionQuery<R>,
|
|
): PermissionQuery {
|
|
return fn({ or, and });
|
|
}
|
|
|
|
/**
|
|
* buildUnkeyQuery is preloaded with out available roles and ensures typesafety for root key validation
|
|
*/
|
|
export const buildUnkeyQuery = buildQuery<z.infer<typeof unkeyPermissionValidation>>;
|