Tutorial: verifying the Svelte adapter headlessly
Same pattern as every other frontend adapter in this series: drive the real primitives directly, no dev server needed.
The component
<script lang="ts">
import { RBACClient } from 'rbac-fs/client';
import { createCanAction, createPermissionStore } from 'rbac-fs/svelte';
export let ownerId: string;
const client = new RBACClient({
user: { id: 'u1' },
permissions: [{ resource: 'invoice', actions: ['approve'] }],
conditions: [{ resource: 'expense-report', actions: ['approve'], when: 'owner_id == user.id' }],
});
const permissions = createPermissionStore(client);
const can = createCanAction(client);
</script>
<button use:can={{ a: 'invoice', I: 'approve' }}>Approve invoice</button>
<button use:can={{ a: 'expense-report', I: 'approve', context: { owner_id: ownerId } }}>
Approve my expense report
</button>
<p>Imperative check: {$permissions('invoice', 'approve') ? 'allowed' : 'denied'}</p>Verify the store directly
import { get } from 'svelte/store';
console.log(get(permissions)('invoice', 'approve')); // true — same as client.can('invoice', 'approve')get(permissions) returns the store's current value, which is client.can itself — calling it is identical to calling client.can directly, so this line is really just confirming the store wrapper didn't change the underlying function's behavior.
Verify the action directly
function makeNode() {
return { style: { display: '' } };
}
const node = makeNode();
const action = can(node, { a: 'invoice', I: 'approve' });
console.log(node.style.display); // '' — visible, permission granted
const deniedNode = makeNode();
can(deniedNode, { a: 'invoice', I: 'delete' }); // no delete permission on this client
console.log(deniedNode.style.display); // 'none'
action.destroy?.(); // Svelte calls this automatically when the element unmountsuse:can follows Svelte's action contract (a function receiving the node and params, optionally returning update/destroy), which is how it's able to toggle display: none on the actual DOM node without any framework-level reactivity beyond what Svelte already provides for actions.
Verified against examples/15-svelte-usage.svelte and its headless counterpart examples/15-svelte-usage-verify.mjs.