Svelte
permdock/svelte maps the snapshot-backed provider, hook and guard onto Svelte context, a readable permission store and a component.
Status: planned Phase: 2
Purpose
Same model as React: snapshot from the server, local evaluation of portable grants, batched decision endpoint for closure grants. permdock/svelte uses Svelte's context API to hold one client store per component tree and a readable store per permission question so templates can use the $ prefix. Svelte 5 runes are supported through the same store objects; a rune-first API is an open question below. No policy or server module is imported.
API
<!-- +layout.svelte -->
<script lang="ts">
import { setPermDock } from 'permdock/svelte'
let { data, children } = $props()
setPermDock({ snapshot: data.snapshot, endpoint: '/api/permdock' })
</script>
{@render children()}<!-- PostActions.svelte -->
<script lang="ts">
import { getPermDock, permission, Protected } from 'permdock/svelte'
import { permissions } from '$lib/permissions'
let { post } = $props()
const permdock = getPermDock() // can / decide / status / invalidate
const canEdit = permission(permissions.post.update, () => post) // readable store: { allowed, status, decision }
</script>
{#if $canEdit.allowed}<EditButton />{/if}
<Protected permission={permissions.post.update} data={post}>
<EditButton />
{#snippet pending()}<Skeleton />{/snippet}
{#snippet fallback(decision)}<Locked reason={decision.denials[0]?.reason} />{/snippet}
</Protected>| Export | Role |
|---|---|
setPermDock | Called once in a layout. Validates the snapshot, creates the client store and sets it in Svelte context. Options match the React provider (snapshot, endpoint, fetch, headers). |
getPermDock | Reads the store from context: can, decide, status, invalidate, refresh. |
permission | Returns a readable store for one reference and, for instance actions, a getter for the resource. Emits allowed, status, decision; re-evaluates when the resource id changes. |
Protected | Component with permission, data and optional tenant props and children, pending, fallback snippets. |
permissions, filtered | Store factories mirroring usePermissions and useFilter: a readable store of one entry per reference, and a derived store of the rows the subject may act on. |
tenant, memberships, roles, assignable | Readable stores from context for the active tenant (with switchTo on the store object), the membership list, roles held in a tenant and roles the subject may hand out (UI, tenancy). |
approval, subject | A store factory for the approval-required flow and a readable store of the snapshot's subject summary (simulated included). |
In SvelteKit the snapshot is loaded in +layout.server.ts from a request-scoped PermDock, and the decision endpoint is a +server.ts route built on the server kernel.
Request lifecycle
+layout.server.tscreates the request-scoped instance and returnspermdock.snapshot()as page data.setPermDockruns during component initialisation (not in an effect), so the first server render and the first client render already see the snapshot.permission(...)derives its answer synchronously for portable grants; non-portable grants enqueue a batched AuthZENevaluationsrequest toendpointand emitpendinguntil the answer arrives.- Answers are cached by
reference.keyplus resourceid.invalidate(permissions.post)drops that namespace and subscribed stores refetch.
What it validates
- The snapshot against the snapshot v1 schema; on failure the store enters
server-onlymode. - Arity of
permissionat the type level. - Nothing about resource data on the client; posted data is validated by the decision endpoint at the boundary, and the API re-checks every mutation.
How denials surface
$store.allowedisfalseand$store.decisionexplains why (denials,alternatives, orapproval-required).Protectedrenders thefallbacksnippet with theDecision.- The decision endpoint answers with Problem Details; client-side failures degrade to
server-only.
Example app
apps/examples/svelte: SvelteKit with a +layout.server.ts snapshot, a +server.ts decision endpoint via permdock/server, portable and closure-backed checks, invalidate after a form action, and a Vitest browser test for first-render correctness.
Related standards
- AuthZEN, Problem Details, Standard Schema.
- Concepts: snapshots, decisions.
Open questions
- Exact identifier names. The plan is silent for Svelte; this page proposes
setPermDock/getPermDock(Svelte's context idiom), apermissionstore factory andProtected, with store-shaped counterparts of every React hook in the UI parity table (tenant,memberships,roles,assignable,approval,subject,permissions,filtered). A rune-basedusePermissionreturning a reactive object instead of a store is the main alternative once the Svelte 5 baseline is fixed. - Whether the SvelteKit pieces (
loadhelper,+server.tsendpoint factory,handlehook) live inpermdock/svelteor a separatepermdock/sveltekitentry. - How to expose the store outside components (for example in
loadfunctions), since context is component-scoped.