Skip to content

The core engine

@vperms/core is the pure TypeScript engine: pattern matching, inheritance traversal, precedence and resolution. It has no dependencies, no validation and no I/O beyond the adapter abstraction. Most applications use the higher-level vperms service instead, but the core is public and useful when you need the building blocks directly.

Everything the engine needs from storage goes through one abstract class:

import type { PermissionGrant, Subject } from "@vperms/core";
abstract class VeguiPermsAdapter {
abstract findSubject(workspaceId: string, subjectId: string): Promise<Subject | null>;
abstract saveSubject(workspaceId: string, subject: Subject): Promise<Subject>;
abstract deleteSubject(workspaceId: string, subjectId: string): Promise<boolean>;
abstract findSubjectGrants(workspaceId: string, subjectId: string): Promise<PermissionGrant[]>;
abstract grantPermission(
workspaceId: string,
subjectId: string,
permission: string,
value: boolean,
): Promise<PermissionGrant>;
abstract ungrantPermission(
workspaceId: string,
subjectId: string,
permission: string,
): Promise<boolean>;
}

An adapter only stores and retrieves data. It performs no validation, resolution, inheritance or evaluation — the engine and service handle all of that. See Writing a custom adapter.

import { matchesPattern, matchPermission } from "@vperms/core";
matchesPattern("workspaces.1.*", "workspaces.1.read"); // true
matchPermission(
[
{ permission: "workspaces.*", value: true, subjectId: "u", workspaceId: "w" },
{ permission: "workspaces.1.*", value: false, subjectId: "u", workspaceId: "w" },
],
"workspaces.1.read",
); // false

matchesPattern is the raw segment matcher. matchPermission sorts with compareGrants and returns the best match, or null.

import { compareGrants, permissionSpecificity } from "@vperms/core";
permissionSpecificity("workspaces.*.read"); // 5
permissionSpecificity("workspaces.1.*"); // 4

compareGrants(a, b) orders grants highest-priority-first. See Resolution for the full rule list.

import {
resolveInheritedPermissions,
resolveSubjectPermissions,
canResolved,
} from "@vperms/core";
const inherited = await resolveInheritedPermissions(adapter, workspaceId, subject, {
defaultParents,
});
const effective = await resolveSubjectPermissions(adapter, workspaceId, subject, {
defaultParents,
});
canResolved(effective, "workspaces.1.read");

These are the exact functions the service calls. The vperms package re-exports the most common of them (canResolved, resolveSubjectPermissions, subjectPermissionsPermission, SELF_PERMISSIONS_PERMISSION, the layer constants, and the shared types).

import {
EXPLICIT_PARENT_LAYER, // 0
TYPE_DEFAULT_PARENT_LAYER, // 1
GLOBAL_DEFAULT_PARENT_LAYER, // 2
BUILTIN_PERMISSION_LAYER, // 3
} from "@vperms/core";

Layer numbers are ordered by priority: lower is stronger. See the full API reference.