The service
VeguiPermsService is the application-facing API exported by the vperms
package. It owns validation, permission resolution, inheritance, cycle
protection and evaluation; the adapter only persists data.
import { VeguiPermsMemoryAdapter, VeguiPermsService } from "vperms";
const vperms = new VeguiPermsService({ adapter: new VeguiPermsMemoryAdapter(), defaultParents: { global: ["everyone"] },});Options
Section titled “Options”interface VeguiPermsServiceOptions { adapter: VeguiPermsAdapter; defaultParents?: DefaultParents;}defaultParents is validated with DefaultParentsSchema when the service is
constructed. See Inheritance.
Methods
Section titled “Methods”| Method | Returns | Description |
|---|---|---|
can(workspaceId, subject, permission) |
Promise<boolean> |
Whether the subject may perform the permission. |
resolvePermissions(workspaceId, subject) |
Promise<ResolvedSubject> |
Full effective permission snapshot. |
saveSubject(workspaceId, subject) |
Promise<Subject> |
Create or update a subject. |
deleteSubject(workspaceId, subject) |
Promise<boolean> |
Delete a subject; true when it existed. |
setPermission(workspaceId, subject, permission, value) |
Promise<PermissionGrant> |
Upsert an allow/deny grant. |
unsetPermission(workspaceId, subject, permission) |
Promise<boolean> |
Remove a grant; true when it existed. |
Every method takes the workspace id first and accepts a raw subject id or a
Principal. All public inputs are validated
with Zod; invalid input throws a ZodError.
await vperms.saveSubject("workspace", { id: "user", type: SubjectType.User, parents: ["developers"],});await vperms.setPermission("workspace", "developers", "posts.read", true);
await vperms.can("workspace", "user", "posts.read"); // trueawait vperms.resolvePermissions("workspace", "user");can() returns false when the adapter has no record for the subject.
resolvePermissions() throws SubjectNotFoundError in that case, because the
snapshot needs a subject record. Direct grants short-circuit inheritance; then
explicit, type-default and global-default layers are consulted in order.
Request runtime
Section titled “Request runtime”The runtime helpers are shared by all framework integrations. They hydrate a request-scoped context exactly once per request.
import { createAbility, resolveRequestContext, resolveSubjectId,} from "vperms";
const ability = createAbility(vperms, "workspace", "user");await ability.can("posts.read"); // memoized per permissioncreateAbility(service, workspaceId, subject) returns an Ability whose
can(permission) memoizes the promise, so the same permission is never
evaluated twice within a request.
resolveRequestContext performs the whole dance:
const context = await resolveRequestContext({ adapter, service: vperms, workspaceId: "workspace", subject: await getCurrentUser(), // SubjectId | Principal | null});
context.id; // resolved subject id ("anonymous" when subject was null)context.subject; // Subject record, or undefined when not persistedcontext.kind; // SubjectType, or undefinedcontext.ability; // AbilityWhen the resolver returns null/undefined, the anonymous subject is
loaded — created on demand with SubjectType.Anon via
ensureAnonymousSubject — so defaultParents.byType.anon applies
automatically. ANONYMOUS_SUBJECT_ID is "anonymous".
The Ability interface is intentionally minimal:
interface Ability { can(permission: string): Promise<boolean>;}Middleware, guards, decorators and permission-export handlers all reuse the same instance.