Roles that build on roles
One role extends another, one is cut out of another, and what a deny does and does not carry down.
pnpm add @niscorp/charterWhat exists
Charter is always handed the list of what exists. Here it is the ids of the screens the theatre’s app ships. An id with a dot in it, like me.tickets, is just a name: the dot is there so one pattern can select a family.
import { resolvePrincipal } from '@niscorp/charter';
const screens = [
'welcome',
'programme',
'seats',
'me.tickets',
'me.pass',
'door.list',
'members',
'takings',
'settings',
];Extend a role
extends takes everything the named roles select. The usher has two screens. The manager gets those, and adds three by name.
const charter = {
usher: ['programme', 'door.*'],
manager: { extends: ['usher'], allow: ['members', 'takings', 'settings'] },
};
console.log('manager:', [...resolvePrincipal(charter, screens, ['manager'])]);A deny does not carry down
The trainee is an usher without the door list. The lead extends the trainee and allows the door list again.
That works: a deny belongs to the role it is written in, and a role that extends it may add the name back. The verifier, in the next guide, reports this as a warning, so it is always a choice somebody made.
const withTrainee = {
...charter,
trainee: { extends: ['usher'], deny: ['door.list'] },
lead: { extends: ['trainee'], allow: ['door.list', 'members'] },
};
console.log('trainee:', [...resolvePrincipal(withTrainee, screens, ['trainee'])]);
console.log('lead:', [...resolvePrincipal(withTrainee, screens, ['lead'])]);Cut one role out of another
without takes away everything another role selects. It is how a plan or a switch is written: the money screens are a role, and the volunteer manager is a manager without them.
const withMoney = {
...charter,
money: ['takings'],
volunteer: { extends: ['manager'], without: ['money'] },
};
console.log('volunteer:', [...resolvePrincipal(withMoney, screens, ['volunteer'])]);Run it.
The manager’s five screens, the trainee’s one, the lead’s three, and the volunteer manager with the takings cut out.
npx tsx roles.mtsmanager: [ 'programme', 'door.list', 'members', 'takings', 'settings' ] trainee: [ 'programme' ] lead: [ 'programme', 'door.list', 'members' ] volunteer: [ 'programme', 'door.list', 'members', 'settings' ]