Events & Auditing
AccessControl has a small, dependency‑free event emitter. The headline event is
access, which fires on every resolved check (granted and denied) — your
ready‑made audit log.
ac.on('access', (e) => audit(e));ac.on('change', (e) => log(e.type));ac.on('error', (e) => report(e.error));access — the Audit Stream
Section titled “access — the Audit Stream”ac.on('access', (e) => { // { // roles, resource, category, action, possession, // granted, attributes, reason, context, timestamp // } audit(e);});reason explains a denial — one of:
| reason | meaning |
|---|---|
no_grant | no matching grant for the role/resource/action |
condition_failed | a .where() condition didn’t hold |
ownership_failed | an own rule couldn’t verify ownership |
require_failed | a .require() gate didn’t pass |
ac.on('access', (e) => { if (!e.granted) metrics.increment(`denied.${e.reason}`);});change — Policy Edits
Section titled “change — Policy Edits”Fires when the model is mutated (grant, deny, extend, set_grants,
setup, require, remove, reset, lock). The payload carries the type
and a detail:
ac.on('change', (e) => log(`policy ${e.type}`, e.detail));error — Faults
Section titled “error — Faults”Fires when a check or operation throws (carries the AccessControlError). It
fires even under tryCan(),
where the check itself returns granted: false — so you still observe the fault.
ac.on('error', (e) => report(e.error)); // e.error is an AccessControlErrorSubscription Management
Section titled “Subscription Management”const onAccess = (e) => audit(e);ac.on('access', onAccess);ac.once('change', (e) => init(e)); // auto-removes after the first eventac.off('access', onAccess); // remove oneac.off('access'); // remove all 'access' listeners