Guards
Guards are the heart of Pulse. They represent Conditioned Truths or business rules. Unlike simple computed values, Guards track their own status (ok, fail, pending) and explicit failure reason.
The Problem
Section titled “The Problem”In traditional state management, you often handle “loading”, “error”, and “success” states manually with separate variables.
// The old waylet isLoading = false;let error = null;let data = null;The Pulse Way
Section titled “The Pulse Way”A Guard encapsulates all of this.
import { guard } from '@pulse-js/core';
const isAuthorized = guard('auth-check', async () => { const user = currentUser(); if (!user) throw 'Not logged in'; if (user.role !== 'admin') return false; // Fail return true; // OK});Guard States
Section titled “Guard States”- OK: The evaluator returned a truthy value (or
true). - FAIL: The evaluator returned
falseor threw an error. - PENDING: The evaluator returned a Promise that is currently resolving, or returned
undefined.
API Reference
Section titled “API Reference”Returns true if the last evaluation was successful.
.fail()
Section titled “.fail()”Returns true if the last evaluation failed.
.pending()
Section titled “.pending()”Returns true if the guard is currently evaluating (async).
.reason()
Section titled “.reason()”Returns the reason for the failure. In Pulse v0.2.2, this is always a structured object.
if (isAuthorized.fail()) { console.error(isAuthorized.reason()); // "Not logged in"}Advanced Control Flow
Section titled “Advanced Control Flow”Sometimes returning false isn’t enough. You might want to fail with a specific error code or return early.
guardFail(reason)
Section titled “guardFail(reason)”Import guardFail to stop execution immediately and mark the guard as failed.
Structured Reasons (Async Friendly): You can pass a structured object to provide more context to your UI.
import { guard, guardFail } from '@pulse-js/core';
const userCheck = guard('user-auth', async () => { const user = await fetchUser();
if (!user) { guardFail({ code: 'AUTH_REQUIRED', message: 'User must be logged in', meta: { redirect: '/login' } }); } return true;});guardOk(value)
Section titled “guardOk(value)”Return early with a success value.
import { guard, guardOk } from '@pulse-js/core';
const check = guard(() => { if (cache.has(key)) return guardOk(cache.get(key)); // ... expensive logic return result;});Guard Composition
Section titled “Guard Composition”Pulse provides powerful ways to compose guards together.
guard.map(source, mapper, name?)
Section titled “guard.map(source, mapper, name?)”Transforms a Source into a Guard. This is the recommended way to derive business logic from state.
import { source, guard } from '@pulse-js/core';
const todos = source([{ done: false }, { done: true }]);
// Reactive guard derived from sourceconst doneCount = guard.map(todos, list => list.filter(t => t.done).length);Logical Compositions
Section titled “Logical Compositions”guard.all(name, [guards]): Succeeds if all guards succeed.guard.any(name, [guards]): Succeeds if at least one guard succeeds.guard.not(name, guard): Inverts the status of a guard.
Debugging
Section titled “Debugging”.explain()
Section titled “.explain()”Returns a complete snapshot of the guard’s state and its recursive dependency tree.
[!NOTE] For failed guards, the dependency tree now includes the specific
reasonwhy each sub-dependency failed, making debugging much easier.
const explanation = canPlaceOrder.explain();console.log(explanation);
/* Output:{ name: 'can-place-order', status: 'fail', reason: 'Insufficient balance', dependencies: [ { name: 'has-items', status: 'ok' }, { name: 'sufficient-balance', status: 'fail', reason: 'Balance is too low' } ]}*/Type Helpers
Section titled “Type Helpers”InferGuardType<T>
Section titled “InferGuardType<T>”Extract the result type from a Guard instance for better TypeScript integration.
import { guard, type InferGuardType } from '@pulse-js/core';
const userGuard = guard('user', async () => ({ name: 'Alice' }));type User = InferGuardType<typeof userGuard>; // { name: string }.state()
Section titled “.state()”Returns the raw internal state object ({ status, value, reason, lastReason }).
._evaluate()
Section titled “._evaluate()”Manually forces the guard to re-run its evaluator function.