Skip to content

Vanilla client

@vperms/client is a framework-independent client for the resolved-permission DTO. Resolved subjects are plain JSON, so they can be evaluated without a server, an adapter or any inheritance logic.

import { createVPerms } from "@vperms/client";
const vperms = createVPerms("https://api.example.com", {
prefix: "/vperms", // default
subjectResolver: async () => currentUser,
fetchOptions: { credentials: "include" },
});
const ability = await vperms.getAbility();
ability.can("workspaces.1.read"); // synchronous, same result as server can()
interface VPermsConfig {
prefix?: string; // default "/vperms"
subjectResolver?: () => SubjectId | Principal | null | Promise<...>;
fetch?: FetchLike; // default globalThis.fetch
fetchOptions?: RequestInit;
}
  • origin may be absolute (https://api.example.com), relative (/api) or empty ("" for the current origin).
  • subjectResolver resolves the current subject. Returning null (or omitting it) loads the anonymous subject through /subject/me.
interface VPermsClient {
readonly origin: string;
readonly prefix: string;
getResolvedSubject(subjectId?: SubjectId | Principal): Promise<ResolvedSubject>;
getAbility(subjectId?: SubjectId | Principal): Promise<PermissionAbility>;
}

With no explicit subject, the client requests {origin}{prefix}/subject/me; with one, it requests {origin}{prefix}/subject/:subjectId. Responses are validated with ResolvedSubjectSchema before use.

import { createAbility, fetchResolvedSubject } from "@vperms/client";
const ability = createAbility(resolvedSubject);
ability.subject; // ResolvedSubject snapshot (immutable)
ability.permissions; // ResolvedPermission[]
ability.can("posts.read");

PermissionAbility (also exported as Ability) wraps a single snapshot. Its can() is synchronous and only evaluates ResolvedPermission[] with the same matcher and weight precedence as canResolved(). It knows nothing about parents, default parents, !parent, inheritance depth or adapters.

import { fetchResolvedSubject, VPermsHttpError } from "@vperms/client";
const me = await fetchResolvedSubject("/api/vperms/subject/me");

fetchResolvedSubject(url, { fetch, requestInit }) is what the client uses internally: it validates the body with ResolvedSubjectSchema and throws VPermsHttpError (with status and url) on non-2xx responses. Plug in fetch for SSR, cookie forwarding or tests.