Skip to content

VexAccessConfigInput

Defined in: packages/core/src/access/types.ts:907

Input shape for the defineAccess builder.

TUserCollection

{ slug } shape naming the user collection.

TOrgCollection

{ slug } shape naming the org collection; undefined if absent.

VexAccessConfig for the resolved runtime shape.

TRoles extends readonly string[]

Tuple of role name literals.

TResources extends readonly AccessResource[] = readonly AccessResource[]

Structural resource tuple ({ slug, versions? }).

TCustomResources extends Record<string, CustomResourceInput> = { }

Custom resource declarations.

TUserSlug extends CollectionSlug = CollectionSlug

TOrgSlug extends CollectionSlug | undefined = undefined

TCustomActions extends Partial<Record<TResources[number]["slug"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>> = { }

TPermissions = Record<TRoles[number], RolePermissions<SubjectMap<TResources, TCustomResources, TUserSlug, TOrgSlug, TCustomActions>, InferDocTypeFromSlug<TUserSlug>, TOrgSlug extends string ? InferDocTypeFromSlug<TOrgSlug> : never, TUserSlug, TOrgSlug>>

The inferred permissions literal, which is what makes each callback’s return type inspectable by ValidateFieldMaps.

optional anonRole?: TRoles[number]

Defined in: packages/core/src/access/types.ts:936

OPTIONAL. Role applied when a caller’s roles resolve empty — no session, or an anonymous user (e.g. Better Auth anonymous plugin) whose userRolesField is unset. Explicit roles always win over this fallback. Omitted → empty roles deny, exactly as before.


optional customActions?: TCustomActions & Partial<Record<TUserSlug | TResources[number]["slug"] | Extract<TOrgSlug, string>, CustomActionsInput>> & { [K in string | number | symbol]: never }

Defined in: packages/core/src/access/types.ts:965

Extra actions per resource, beyond CRUD. Keys must be declared resource slugs (or the user/org collection slugs).

Three constituents, each load-bearing:

  1. TCustomActions — the inference site; carries the caller’s literal declaration into the phantom SubjectMap.
  2. Partial<Record<slugs, …>> — the COMPLETION source. Key completions come from the property’s contextual type, and a bare unresolved type parameter offers nothing; this constituent names the slug union directly, which is resolvable in the first inference round (resources and the slugs are not context-sensitive). Redundant for checking — the bound already constrains values.
  3. The exactness map — the TYPO guard. The Partial<Record<…>> bound cannot reject a bad key on its own: an all-optional target is a “weak type”, so TypeScript errors only when the object shares ZERO keys with it — one valid entry beside a garbage key passes, because structural subtyping permits extra properties and per-key excess checking does not fire against a generic-constrained inference. Mapping every key NOT in the slug union to never makes the garbage entry’s value unassignable AT ITS OWN KEY, restoring both the error and its location.

optional customResources?: TCustomResources

Defined in: packages/core/src/access/types.ts:981

Custom, non-resource subjects with arbitrary action unions and optional typed data. Example: { apiKeys: { actions: ["create", "revoke"] } }.


optional enabled?: boolean

Defined in: packages/core/src/access/types.ts:928

Default: true. Turn access control on or off.


optional organizations?: boolean

Defined in: packages/core/src/access/types.ts:1028

Default: false. Whether organizations are an ENFORCED tenancy boundary in this project, not just available data — developer-approved, binding (the pre-existing behavior keyed orgCollectionSlug alone into view scoping, which silently broke a project that only declared orgCollectionSlug for its own callbacks’ benefit, with no org data ever seeded). Currently drives ONE thing: whether saved (vex_views) shared views are scoped to the caller’s own organization at all (listViews/loadSharedSavedListViewWheres/saveView/removeView, server, and useCanAccessSharedSavedListViews, client — all resolve this through the SAME shared predicate, isViewOrganizationScopingEnabled, so none of them can disagree). Requires orgCollectionSlug to be set too — this flag has no effect without it.

When false (the default): shared-view CRUD is exactly the pre-organization-scoping behavior — any caller with the relevant VIEW_ACTIONS permission can create, read, update, and delete ANY shared view, including an org-less one, regardless of organization.


optional orgCollectionSlug?: TOrgSlug

Defined in: packages/core/src/access/types.ts:1007

Slug of the organization collection. When present, organization is available (typed via the registry) in every permission callback; when omitted, callbacks have no organization key. Declaring this alone does NOT scope saved views (or anything else) to the caller’s own organization — it only says WHERE organization data lives, for a project’s own permission callbacks to read. Set organizations to actually enforce organization-scoped behavior.


permissions: TPermissions & ValidateFieldMaps<TPermissions, SubjectMap<TResources, TCustomResources, TUserSlug, TOrgSlug, TCustomActions>> & { [K in string | number | symbol]: never }

Defined in: packages/core/src/access/types.ts:1040

Permission matrix: role → subject → check. See RolePermissions for shapes and wildcard semantics.

Intersected with ValidateFieldMaps so a returned field map naming an unknown field is a compile error at that entry. The bare TPermissions member is what makes the literal inferrable; the constraint on TPermissions (declared on defineAccess) is what keeps every callback’s props contextually typed.


resources: TResources

Defined in: packages/core/src/access/types.ts:942

Collections/globals contributing subjects, keyed by slug.


roles: TRoles

Defined in: packages/core/src/access/types.ts:939

Role identifiers; keys of the permissions matrix.


userCollectionSlug: TUserSlug

Defined in: packages/core/src/access/types.ts:989

Slug of the collection whose documents are user in callbacks. A plain slug string — the full collection often does not exist at authoring time (auth-adapter collections merge later, inside defineConfig); the document type resolves from the generated registry by slug.


userRolesField: string

Defined in: packages/core/src/access/types.ts:996

REQUIRED. The field on the user document that holds the user’s role(s). Value may be string or string[]; hasPermission normalizes both. Callers never pass roles separately — they always ride the user document.