or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions.mdactors.mdgraph-utilities.mdguards.mdindex.mdstate-machines.md

guards.mddocs/

0

# Guards

1

2

Guard functions and combinators for conditional logic in state machine transitions and actions. Guards provide type-safe predicate functions that determine whether transitions should be taken or actions should be executed.

3

4

## Capabilities

5

6

### Guard Predicates

7

8

Core guard predicate functions for evaluating conditions based on context and events.

9

10

```typescript { .api }

11

/**

12

* Function type for guard predicates that evaluate conditions

13

* @param args - Guard arguments containing context and event

14

* @param params - Optional parameters passed to the guard

15

* @returns Boolean indicating whether the guard condition is met

16

*/

17

type GuardPredicate<

18

TContext,

19

TExpressionEvent extends EventObject,

20

TParams = undefined,

21

TGuard = any

22

> = {

23

(args: GuardArgs<TContext, TExpressionEvent>, params: TParams): boolean;

24

_out_TGuard?: TGuard;

25

};

26

27

interface GuardArgs<TContext, TExpressionEvent extends EventObject> {

28

/** Current machine context */

29

context: TContext;

30

/** Current event being processed */

31

event: TExpressionEvent;

32

}

33

34

type Guard<TContext, TExpressionEvent extends EventObject, TParams, TGuard> =

35

| GuardPredicate<TContext, TExpressionEvent, TParams, TGuard>

36

| { type: string; params?: TParams }

37

| string;

38

39

type UnknownGuard = Guard<any, any, any, any>;

40

```

41

42

### Logical Combinators

43

44

Logical operators for combining multiple guards into complex conditional logic.

45

46

```typescript { .api }

47

/**

48

* Logical AND guard combinator - evaluates to true if all guards evaluate to true

49

* @param guards - Array of guards to combine with AND logic

50

* @returns GuardPredicate that returns true when all guards are true

51

*/

52

function and<TContext, TExpressionEvent extends EventObject>(

53

guards: Guard<TContext, TExpressionEvent, any, any>[]

54

): GuardPredicate<TContext, TExpressionEvent, unknown, any>;

55

56

/**

57

* Logical OR guard combinator - evaluates to true if any guard evaluates to true

58

* @param guards - Array of guards to combine with OR logic

59

* @returns GuardPredicate that returns true when any guard is true

60

*/

61

function or<TContext, TExpressionEvent extends EventObject>(

62

guards: Guard<TContext, TExpressionEvent, any, any>[]

63

): GuardPredicate<TContext, TExpressionEvent, unknown, any>;

64

65

/**

66

* Logical NOT guard combinator - evaluates to true if the guard evaluates to false

67

* @param guard - Guard to negate

68

* @returns GuardPredicate that returns the opposite of the input guard

69

*/

70

function not<TContext, TExpressionEvent extends EventObject>(

71

guard: Guard<TContext, TExpressionEvent, any, any>

72

): GuardPredicate<TContext, TExpressionEvent, unknown, any>;

73

```

74

75

**Usage Examples:**

76

77

```typescript

78

import { and, or, not } from "xstate/guards";

79

80

// Combine guards with AND logic

81

and([

82

({ context }) => context.isLoggedIn,

83

({ context }) => context.hasPermission,

84

({ event }) => event.type === "SUBMIT"

85

])

86

87

// Combine guards with OR logic

88

or([

89

({ context }) => context.isAdmin,

90

({ context }) => context.isOwner,

91

({ event }) => event.force === true

92

])

93

94

// Negate a guard

95

not(({ context }) => context.isDisabled)

96

97

// Complex combinations

98

and([

99

({ context }) => context.user != null,

100

or([

101

({ context }) => context.user.role === "admin",

102

({ context }) => context.user.role === "moderator"

103

]),

104

not(({ context }) => context.user.suspended)

105

])

106

```

107

108

### State Checking

109

110

Guard for checking if the machine is currently in specific states.

111

112

```typescript { .api }

113

/**

114

* Guard to check if machine is in specific state(s)

115

* @param stateValue - State value to check (string, object, or state ID)

116

* @returns GuardPredicate that returns true when machine matches the state

117

*/

118

function stateIn<TContext, TExpressionEvent extends EventObject>(

119

stateValue: StateValue

120

): GuardPredicate<TContext, TExpressionEvent, any, any>;

121

122

type StateValue = string | { [key: string]: StateValue };

123

```

124

125

**Usage Examples:**

126

127

```typescript

128

import { stateIn } from "xstate/guards";

129

130

// Check simple state

131

stateIn("loading")

132

133

// Check nested state

134

stateIn({ editing: "text" })

135

136

// Check multiple possible states

137

stateIn({ form: { validation: "pending" } })

138

139

// Use in transitions

140

{

141

on: {

142

SUBMIT: {

143

guard: stateIn("ready"),

144

target: "submitting"

145

}

146

}

147

}

148

```

149

150

### Guard Evaluation

151

152

Low-level function for evaluating guards in different contexts.

153

154

```typescript { .api }

155

/**

156

* Evaluates a guard against current snapshot and context

157

* @param guard - Guard to evaluate (function, object, or string reference)

158

* @param context - Current machine context

159

* @param event - Current event

160

* @param snapshot - Current machine snapshot

161

* @returns Boolean result of guard evaluation

162

*/

163

function evaluateGuard<TContext, TExpressionEvent extends EventObject>(

164

guard: Guard<TContext, TExpressionEvent, any, any>,

165

context: TContext,

166

event: TExpressionEvent,

167

snapshot: AnyMachineSnapshot

168

): boolean;

169

```

170

171

**Usage Examples:**

172

173

```typescript

174

import { evaluateGuard } from "xstate/guards";

175

176

// Evaluate guard manually

177

const isValid = evaluateGuard(

178

({ context, event }) => context.count > event.threshold,

179

{ count: 10 },

180

{ type: "CHECK", threshold: 5 },

181

currentSnapshot

182

);

183

184

// Evaluate complex guard

185

const hasAccess = evaluateGuard(

186

and([

187

({ context }) => context.user?.active,

188

({ context }) => context.permissions.includes("read")

189

]),

190

context,

191

event,

192

snapshot

193

);

194

```

195

196

## Guard Configuration

197

198

### Inline Guards

199

200

Guards defined directly in machine configuration as functions.

201

202

```typescript

203

// Inline guard function

204

{

205

on: {

206

NEXT: {

207

guard: ({ context, event }) => context.step < event.maxSteps,

208

target: "nextStep"

209

}

210

}

211

}

212

```

213

214

### Named Guards

215

216

Guards defined in machine implementations and referenced by name.

217

218

```typescript

219

const machine = setup({

220

guards: {

221

canProceed: ({ context }) => context.isReady && context.hasData,

222

isAuthorized: ({ context, event }) => {

223

return context.user?.permissions?.includes(event.requiredPermission);

224

}

225

}

226

}).createMachine({

227

on: {

228

PROCEED: {

229

guard: "canProceed", // Reference by name

230

target: "processing"

231

},

232

SECURE_ACTION: {

233

guard: {

234

type: "isAuthorized",

235

params: { requiredPermission: "admin" }

236

},

237

target: "adminPanel"

238

}

239

}

240

});

241

```

242

243

### Parameterized Guards

244

245

Guards that accept parameters for reusable conditional logic.

246

247

```typescript

248

const machine = setup({

249

guards: {

250

greaterThan: ({ context }, { value }) => context.count > value,

251

hasRole: ({ context }, { role }) => context.user?.role === role,

252

withinTimeframe: ({ context }, { hours }) => {

253

const now = Date.now();

254

const diff = now - context.timestamp;

255

return diff < (hours * 60 * 60 * 1000);

256

}

257

}

258

}).createMachine({

259

on: {

260

CHECK_COUNT: {

261

guard: { type: "greaterThan", params: { value: 10 } },

262

actions: "processHigh"

263

},

264

ADMIN_ACTION: {

265

guard: { type: "hasRole", params: { role: "admin" } },

266

actions: "executeAdmin"

267

},

268

TIME_SENSITIVE: {

269

guard: { type: "withinTimeframe", params: { hours: 24 } },

270

actions: "handleUrgent"

271

}

272

}

273

});

274

```

275

276

### Dynamic Parameters

277

278

Guards with parameters resolved at runtime based on context and event.

279

280

```typescript

281

{

282

on: {

283

VALIDATE: {

284

guard: {

285

type: "meetsThreshold",

286

params: ({ context, event }) => ({

287

threshold: context.dynamicThreshold,

288

multiplier: event.urgency || 1

289

})

290

},

291

target: "validated"

292

}

293

}

294

}

295

```

296

297

## Advanced Guard Patterns

298

299

### Multi-Level Conditions

300

301

Complex nested guard logic for sophisticated conditional behavior.

302

303

```typescript

304

import { and, or, not, stateIn } from "xstate/guards";

305

306

const complexGuard = and([

307

// User must be authenticated

308

({ context }) => context.user != null,

309

310

// Must be in correct state

311

stateIn({ form: "editing" }),

312

313

// Either admin OR (owner AND not suspended)

314

or([

315

({ context }) => context.user.role === "admin",

316

and([

317

({ context }) => context.user.isOwner,

318

not(({ context }) => context.user.suspended)

319

])

320

]),

321

322

// Time-based condition

323

({ context }) => {

324

const now = Date.now();

325

return now - context.lastAction < 300000; // 5 minutes

326

}

327

]);

328

```

329

330

### Form Validation Guards

331

332

Common patterns for form validation and user input checking.

333

334

```typescript

335

const formGuards = {

336

// Field validation

337

hasValidEmail: ({ context }) => {

338

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

339

return emailRegex.test(context.form.email);

340

},

341

342

hasValidPassword: ({ context }) => {

343

const { password } = context.form;

344

return password && password.length >= 8;

345

},

346

347

passwordsMatch: ({ context }) => {

348

const { password, confirmPassword } = context.form;

349

return password === confirmPassword;

350

},

351

352

// Complete form validation

353

formIsValid: and([

354

({ context }) => context.form.email?.trim().length > 0,

355

"hasValidEmail",

356

"hasValidPassword",

357

"passwordsMatch"

358

]),

359

360

// Submission readiness

361

canSubmit: and([

362

"formIsValid",

363

not(stateIn("submitting")),

364

({ context }) => !context.hasErrors

365

])

366

};

367

```

368

369

### Permission-Based Guards

370

371

Role and permission checking patterns for access control.

372

373

```typescript

374

const permissionGuards = {

375

// Role checks

376

isAdmin: ({ context }) => context.user?.role === "admin",

377

isModerator: ({ context }) => context.user?.role === "moderator",

378

isOwner: ({ context, event }) => context.user?.id === event.resourceOwnerId,

379

380

// Permission checks

381

hasPermission: ({ context }, { permission }) => {

382

return context.user?.permissions?.includes(permission);

383

},

384

385

// Complex access control

386

canEdit: or([

387

"isAdmin",

388

and([

389

"isOwner",

390

({ context }) => !context.resource?.locked

391

])

392

]),

393

394

canDelete: and([

395

or(["isAdmin", "isOwner"]),

396

({ context }) => context.resource?.status !== "published"

397

])

398

};

399

```

400

401

## Type Safety

402

403

Guards maintain full type safety with proper TypeScript integration:

404

405

```typescript

406

interface Context {

407

count: number;

408

user: { name: string; role: "admin" | "user" } | null;

409

}

410

411

type Events =

412

| { type: "INCREMENT"; amount: number }

413

| { type: "CHECK_ROLE"; requiredRole: string };

414

415

// Type-safe guard with proper context and event types

416

const typedGuard: GuardPredicate<Context, Events> = ({ context, event }) => {

417

// context and event are properly typed

418

if (event.type === "INCREMENT") {

419

return context.count + event.amount < 100;

420

}

421

if (event.type === "CHECK_ROLE") {

422

return context.user?.role === event.requiredRole;

423

}

424

return false;

425

};

426

```