Logic Composition
Pulse encourages building small, atomic logic units and composing them into larger rules. This keeps your business logic testable and readable.
guard.map(source, mapper, name?)
Section titled “guard.map(source, mapper, name?)”Transforms a Source into a Guard with business logic and full status tracking.
Perfect for:
- Deriving computed values with error handling
- Transforming data with validation
- Business rules that depend on source data
const todos = source([ { done: false, text: 'Task 1' }, { done: true, text: 'Task 2' }], { name: 'todos' });
// Transform source into guard with business logicconst doneCount = guard.map(todos, list => list.filter(t => t.done).length);
// doneCount is a Guard<number> with full status trackingconsole.log(doneCount()); // 1console.log(doneCount.ok()); // trueAsync mappers:
const userId = source(1, { name: 'userId' });
const userName = guard.map(userId, async (id) => { const user = await fetchUser(id); if (!user) throw new Error('User not found'); return user.name;});
// userName.state().status === 'pending' initially// Then 'ok' with value, or 'fail' with reasonError handling:
const data = source([1, 2, 3], { name: 'data' });
const validated = guard.map(data, list => { if (list.length === 0) { return guardFail('List cannot be empty'); } return list.reduce((a, b) => a + b, 0);});guard.all(name, [guards])
Section titled “guard.all(name, [guards])”Passes only if ALL provided guards have a status of ok.
If any guard fails, the composed guard fails with the reason of the first failing dependency.
const canLaunch = guard.all('can-launch', [ isSystemReady, isFuelFull, isClearWeather]);guard.any(name, [guards])
Section titled “guard.any(name, [guards])”Passes if AT LEAST ONE guard has a status of ok.
const hasAccess = guard.any('access-allowed', [ isAdmin, isOwner, hasGuestPass]);guard.not(name, guard)
Section titled “guard.not(name, guard)”Inverts the status of the target guard.
- If target is
ok->guard.notisfail. - If target is
fail->guard.notisok.
const isLocked = guard.not('is-locked', isOpen);Nesting
Section titled “Nesting”Because these composed units are just standard Guards, you can nest them infinitely.
const canDelete = guard.all('can-delete', [ isAuthenticated, guard.any('permission', [isAdmin, isOwner])]);Debugging with explain()
Section titled “Debugging with explain()”All guards provide an explain() method that shows the full dependency tree with reasons:
const canAccess = guard.all('can-access', [ isLoggedIn, hasPermission, isNotBanned]);
const explanation = canAccess.explain();console.log(explanation);// {// name: 'can-access',// status: 'fail',// reason: 'User is banned',// dependencies: [// { name: 'is-logged-in', type: 'guard', status: 'ok' },// { name: 'has-permission', type: 'guard', status: 'ok' },// { name: 'is-not-banned', type: 'guard', status: 'fail', reason: 'User is banned' }// ]// }