### Setup Robot Hooks Tests Source: https://github.com/matthewp/robot/blob/main/packages/robot-hooks/test/test.html Initializes QUnit and imports the test file. Ensure this runs before QUnit starts. ```javascript QUnit.config.autostart = false; { "imports": { "haunted": "../../../node_modules/haunted/lib/haunted.js", "lit": "../../../node_modules/lit/index.js", "lit/async-directive.js": "../../../node_modules/lit/async-directive.js", "lit/directive.js": "../../../node_modules/lit/directive.js", "@lit/reactive-element": "../../../node_modules/@lit/reactive-element/reactive-element.js", "lit-html": "../../../node_modules/lit-html/lit-html.js", "lit-html/async-directive.js": "../../../node_modules/lit-html/async-directive.js", "lit-html/directive.js": "../../../node_modules/lit-html/directive.js", "lit-html/is-server.js": "../../../node_modules/lit-html/is-server.js", "lit-element/lit-element.js": "../../../node_modules/lit-element/lit-element.js", "robot3": "../../../node_modules/robot3/machine.js" } } import './test.js'; QUnit.start(); ``` -------------------------------- ### Install react-robot with npm Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/react-robot.md Install the `react-robot` package using npm. ```bash npm install react-robot --save ``` -------------------------------- ### Install react-robot with Yarn Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/react-robot.md Install the `react-robot` package using Yarn. ```bash yarn add react-robot ``` -------------------------------- ### Complete Robot Machine Example Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/core-concepts.md Use this example to see how states, transitions, guards, actions, and interpretation work together. It sets up a machine with idle, loading, loaded, and error states, and demonstrates triggering transitions. ```javascript import { createMachine, state, transition, guard, action, interpret } from 'robot3'; // Actions const setUser = action((ctx, event) => ({ ...ctx, user: event.data })); const clearError = action((ctx) => ({ ...ctx, error: null })); const setError = action((ctx, event) => ({ ...ctx, error: event.error })); // Guards const isValid = guard((ctx, event) => event.data && event.data.email); // Machine definition const machine = createMachine({ idle: state( transition('fetch', 'loading', clearError) ), loading: state( transition('success', 'loaded', guard(isValid), setUser ), transition('failure', 'error', setError) ), loaded: state( transition('refresh', 'loading') ), error: state( transition('retry', 'loading', clearError) ) }, { user: null, error: null }); // Create and use service const service = interpret(machine, () => { console.log('Current state:', service.machine.current); console.log('Context:', service.context); }); // Trigger transitions service.send('fetch'); service.send({ type: 'success', data: { email: 'user@example.com' } }); ``` -------------------------------- ### Install preact-robot using Yarn Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/preact-robot.md Install the preact-robot package using Yarn. ```bash yarn add preact-robot ``` -------------------------------- ### Install robot3 and Integrations Source: https://context7.com/matthewp/robot/llms.txt Install the core robot3 library and any desired framework-specific integration packages using npm. ```bash npm install robot3 # Integrations (install the ones you need) npm install react-robot npm install preact-robot npm install haunted-robot npm install lit-robot npm install robot-hooks ``` -------------------------------- ### Install lit-robot with npm Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/lit-robot.md Install the lit-robot package using npm. ```bash npm install lit-robot --save ``` -------------------------------- ### Install lit-robot with Yarn Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/lit-robot.md Install the lit-robot package using Yarn. ```bash yarn add lit-robot ``` -------------------------------- ### Install preact-robot using npm Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/preact-robot.md Install the preact-robot package using npm. ```bash npm install preact-robot --save ``` -------------------------------- ### Create a Haunted Component with Robot Hooks Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/robot-hooks.md This example demonstrates how to create a custom element using Haunted, integrating it with Robot hooks to manage the state of a simple machine. It shows the setup for `createUseMachine` and its usage within a functional component. ```javascript import { createMachine, state, transition } from 'robot3'; import { component, useEffect, useState, html } from 'haunted'; import { createUseMachine } from 'robot-hooks'; const useMachine = createUseMachine(useEffect, useState); const machine = createMachine({ idle: state( transition('click', 'active') ), active: state() }); function App() { const [current, send] = useMachine(machine); return html` `; } customElements.define('my-app', component(App)); ``` -------------------------------- ### Install svelte-robot-factory Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/svelte-robot-factory.md Install the svelte-robot-factory and robot3 packages using npm or yarn. ```bash npm install svelte-robot-factory robot3 --save ``` ```bash yarn add svelte-robot-factory robot3 ``` -------------------------------- ### Create a Basic Machine with Transitions Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/composition.md Define a machine with states and transitions that update context. This example shows a simple form field pattern. ```javascript import { createMachine, state, reduce, transition } from 'robot3'; const machine = createMachine({ form: state( transition('first', 'form', reduce((ctx, ev) => ({ ...ctx, first: ev.event.target.value })) ), transition('last', 'form', reduce((ctx, ev) => ({ ...ctx, last: ev.event.target.value })) ) ) }); ``` -------------------------------- ### Import and Setup for Preact Robot Tests Source: https://github.com/matthewp/robot/blob/main/packages/preact-robot/test/test.html This snippet shows the necessary imports and QUnit configuration for running tests with preact-robot. Ensure all paths are correct for your project structure. ```javascript QUnit.config.autostart = false; { "imports": { "htm": "../../../node_modules/htm/dist/htm.module.js", "htm/preact": "../../../node_modules/htm/preact/index.module.js", "preact/hooks": "../../../node_modules/preact/hooks/dist/hooks.module.js", "preact": "../../../node_modules/preact/dist/preact.module.js", "robot3": "../../core/machine.js", "robot-hooks": "../../robot-hooks/machine.js" } } import './test.js'; QUnit.start(); ``` -------------------------------- ### Set Initial State (Explicit) Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-state.md You can explicitly specify which state the machine should start in by providing the initial state name as the first argument to `createMachine`. ```javascript const machine = createMachine('active', { idle: state(), active: state() // Machine starts here }); ``` -------------------------------- ### Install haunted-robot via Yarn Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/haunted-robot.md Install the haunted-robot package using Yarn for use in your project. ```bash yarn add haunted-robot ``` -------------------------------- ### Setup Haunted Robot Tests with QUnit Source: https://github.com/matthewp/robot/blob/main/packages/haunted-robot/test/test.html Configures QUnit for testing and imports necessary modules for haunted-robot. Ensure QUnit is available globally. ```javascript QUnit.config.autostart = false; { "imports": { "haunted": "../../../node_modules/haunted/lib/haunted.js", "lit": "../../../node_modules/lit/index.js", "lit/async-directive.js": "../../../node_modules/lit/async-directive.js", "lit/directive.js": "../../../node_modules/lit/directive.js", "@lit/reactive-element": "../../../node_modules/@lit/reactive-element/reactive-element.js", "lit-html": "../../../node_modules/lit-html/lit-html.js", "lit-html/async-directive.js": "../../../node_modules/lit-html/async-directive.js", "lit-html/directive.js": "../../../node_modules/lit-html/directive.js", "lit-html/is-server.js": "../../../node_modules/lit-html/is-server.js", "lit-element/lit-element.js": "../../../node_modules/lit-element/lit-element.js", "robot3": "../../../node_modules/robot3/machine.js", "robot-hooks": "../../../node_modules/robot-hooks/machine.js" } } import './test.js'; QUnit.start(); ``` -------------------------------- ### Install haunted-robot via npm Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/haunted-robot.md Install the haunted-robot package using npm for use in your project. ```bash npm install haunted-robot --save ``` -------------------------------- ### Action Execution Order Example Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-actions.md Demonstrates the sequence of guards and actions during a transition. Guards are checked first, followed by actions in the order they are defined. The context is updated sequentially by each action. ```javascript const machine = createMachine({ idle: state( transition('submit', 'processing', guard((ctx) => ctx.isValid), // 1. Guard checks action((ctx) => { // 2. First action console.log('First:', ctx.count); return { ...ctx, count: 1 }; }), action((ctx) => { // 3. Second action console.log('Second:', ctx.count); return { ...ctx, count: 2 }; }) ) ), processing: state() // 4. New state with count: 2 }, () => ({ count: 0, isValid: true })); ``` -------------------------------- ### Deterministic Transition Example Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-transitions.md In a given state, a specific event should always lead to the same target state. This ensures predictable behavior. ```javascript loading: state( // From 'loading', the 'done' event ALWAYS goes to 'loaded' transition('done', 'loaded') ) ``` -------------------------------- ### Astro Project Commands Source: https://github.com/matthewp/robot/blob/main/docs/README.md Common commands for managing an Astro project, including installation, development, building, and previewing. ```bash npm install ``` ```bash npm run dev ``` ```bash npm run build ``` ```bash npm run preview ``` ```bash npm run astro ... ``` ```bash npm run astro -- --help ``` -------------------------------- ### Install Robot Hooks and Robot3 Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/robot-hooks.md Install the robot-hooks and robot3 packages using npm or Yarn. These are the core dependencies for using Robot hooks with your state machines. ```bash npm install robot-hooks robot3 --save ``` ```bash yarn add robot-hooks robot3 ``` -------------------------------- ### Dispatching an event with action Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/action.md Use action to run a function during a transition. This example dispatches a 'toggled' event on an element when transitioning to the 'off' state. ```javascript import { createMachine, action, state, transition } from 'robot3'; function dispatchOn(ctx) { const { element } = ctx; element.dispatchEvent(new CustomEvent('toggled')); } const machine = createMachine({ on: state( transition('toggle', 'off') ), off: state( transition('toggle', 'on', action(dispatchOn) ) ) }, () => ({ element: document.querySelector('#toggler') })); ``` -------------------------------- ### Basic React Robot Hook Usage Source: https://github.com/matthewp/robot/blob/main/packages/react-robot/readme.md Demonstrates how to integrate Robot state machines with React using the `useMachine` hook. This example shows setting up a simple machine and rendering a button that updates its state based on machine transitions. ```javascript import { useMachine } from 'react-robot'; import React from 'react'; import { createMachine, state, transition } from 'robot3'; const machine = createMachine({ one: state( transition('next', 'two') ), two: state() }); function App() { const [current, send] = useMachine(machine); return html` `; } ``` -------------------------------- ### Use useMachine Hook with Preact Source: https://github.com/matthewp/robot/blob/main/packages/preact-robot/readme.md Demonstrates how to use the `useMachine` hook from `preact-robot` to manage a Robot state machine within a Preact component. This example shows setting up a simple machine and interacting with it via a button click. ```javascript import { useMachine } from 'preact-robot'; import { h } from 'preact'; import { html } from 'htm/prect'; import { createMachine, state, transition } from 'robot3'; const machine = createMachine({ one: state( transition('next', 'two') ), two: state() }); function App() { const [current, send] = useMachine(machine); return html` `; } ``` -------------------------------- ### Use Robot Hooks with Initial Context Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/robot-hooks.md This example shows how to use the `useMachine` hook with an initial context object. The context is merged with any derived values from reducers, and its properties can be accessed from the `current.context` object. ```javascript const context = initialContext => ({ ...initialContext, page: 23 }); const machine = createMachine({ one: state( transition('go', 'two') ), two: state() }, context); // ... later function App() { const [current, send] = useMachine(machine, { foo: 'bar' }); const { page } = current.context; console.log(current.name); // "one" console.log(page); // 23 } ``` -------------------------------- ### Preventing Invalid State Changes (FSM Example) Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-transitions.md Shows how explicit transitions in a state machine restrict state changes to defined paths, ensuring validity. ```javascript // With FSM - only valid transitions allowed ✅ const machine = createMachine({ idle: state( transition('fetch', 'loading') // Can't go directly to 'loaded' or 'error' from 'idle' ), loading: state( transition('success', 'loaded'), transition('error', 'error') ) }); ``` -------------------------------- ### Implement Login Form with reduce Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/reduce.md Use `reduce` to update the `login` and `password` properties in the machine's context when 'login' or 'password' events are triggered. This example demonstrates setting form field values directly into the context. ```javascript import { createMachine, reduce, state, transition } from 'robot3'; const machine = createMachine({ idle: state( transition('login', 'idle', reduce((ctx, ev) => ({ ...ctx, login: ev.target.value })) ), transition('password', 'idle', reduce((ctx, ev) => ({ ...ctx, password: ev.target.value })) ), transition('submit', 'complete') ), complete: state() }); ``` -------------------------------- ### Robot Composition Pattern for Reusable Transitions Source: https://context7.com/matthewp/robot/llms.txt Leverage reusable transition factories to eliminate boilerplate and keep machines DRY. This example shows a `field` factory for form inputs that saves values to context. ```javascript import { createMachine, guard, reduce, state, transition } from 'robot3'; import { interpret } from 'robot3'; // Reusable factory: a form field that saves its value to context const field = (name, destinationState, ...extra) => transition(name, destinationState, reduce((ctx, ev) => ({ ...ctx, [name]: ev.target.value })), ...extra ); // Compose a machine from reusable pieces const machine = createMachine({ form: state( field('username', 'form'), field('email', 'form'), field('password', 'form', guard((ctx) => ctx.password?.length >= 8) // extra guard for password ), transition('submit', 'submitting', guard((ctx) => ctx.username && ctx.email && ctx.password) ) ), submitting: state( transition('done', 'success'), transition('error', 'form') ), success: state() }, () => ({ username: '', email: '', password: '' })); const service = interpret(machine, (s) => console.log(s.machine.current)); service.send({ type: 'username', target: { value: 'alice' } }); service.send({ type: 'email', target: { value: 'alice@example.com' } }); service.send({ type: 'password', target: { value: 'hunter2!' } }); service.send('submit'); // → submitting ``` -------------------------------- ### Define States and Transitions Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/state.md Use the `state` and `transition` functions to define states and their transitions within a Robot3 state machine. This example shows a basic machine with 'idle' and 'input' states. ```javascript import { createMachine, state, transition } from 'robot3'; const machine = createMachine({ idle: state( transition('first', 'input') ), input: state( immediate('idle', reduce((ctx, ev) => ({ ...ctx, first: ev.target.value })) ) ) }); ``` -------------------------------- ### Permission Guards for Access Control Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-guards.md Demonstrates using guards to check user permissions before allowing specific actions. This example restricts 'edit' to admins and 'delete' to the resource owner. ```javascript const isAdmin = (ctx) => ctx.user.role === 'admin'; const isOwner = (ctx, ev) => ctx.user.id === ev.resourceOwnerId; const hasEditAccess = (ctx) => ctx.user.permissions.includes('edit'); const machine = createMachine({ viewing: state( transition('edit', 'editing', guard(isAdmin) // Only admins can edit ), transition('delete', 'deleting', guard(isOwner) // Only owner can delete ) ), editing: state(), deleting: state() }); ``` -------------------------------- ### Synchronous Context Update with Actions Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-actions.md Use actions for immediate, synchronous updates to the machine's context based on event data. This example shows updating a 'value' property. ```javascript idle: state( transition('update', 'idle', reduce((ctx, ev) => ({ ...ctx, value: ev.value })) ) ) ``` -------------------------------- ### Self-Documenting State Flow Example Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-transitions.md A state machine definition with explicit transitions serves as a clear map of all possible states and their transitions, making the application's flow self-documenting. ```javascript const authMachine = createMachine({ loggedOut: state( transition('login', 'authenticating') ), authenticating: state( transition('success', 'loggedIn'), transition('failure', 'loggedOut'), transition('timeout', 'loggedOut') ), loggedIn: state( transition('logout', 'loggingOut') ), loggingOut: state( transition('done', 'loggedOut') ) }); ``` -------------------------------- ### Preventing Invalid State Changes (Imperative Example) Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-transitions.md Illustrates how state can change unpredictably in traditional imperative code without explicit state management. ```javascript // Without FSM - state can change anywhere ❌ let currentState = 'idle'; function someFunction() { currentState = 'loaded'; // Can this happen from 'idle'? 🤔 } function anotherFunction() { currentState = 'error'; // Should this be possible? 🤔 } ``` -------------------------------- ### Use useMachine Hook with Haunted and Robot Source: https://github.com/matthewp/robot/blob/main/packages/haunted-robot/readme.md This example demonstrates how to use the `useMachine` hook from `haunted-robot` to integrate a Robot state machine with a Haunted web component. It sets up a simple two-state machine and a button to transition between states, displaying the current state's name. ```javascript import { useMachine } from 'haunted-robot'; import { html, component } from 'haunted'; import { createMachine, state, transition } from 'robot3'; const machine = createMachine({ one: state( transition('next', 'two') ), two: state() }); function App() { const [current, send] = useMachine(machine); return html` `; } customElements.define('my-app', component(App)); ``` -------------------------------- ### Define Haunted Component with Robot State Machine Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/haunted-robot.md This example demonstrates how to define a Haunted component that uses the `useMachine` hook to manage state with a Robot state machine. It includes necessary imports for Robot, Haunted, and Lit-HTML. ```js import { createMachine, state, transition } from 'robot3'; import { component } from 'haunted'; import { html } from 'lit-html'; import { useMachine } from 'haunted-robot'; const machine = createMachine({ off: state( transition('toggle', 'on') ), on: state( transition('toggle', 'off') ) }); function App() { const [current, send] = useMachine(machine); return html`
State: ${current.name}
`; } customElements.define('my-app', component(App)); ``` -------------------------------- ### Create Astro Project with Basics Template Source: https://github.com/matthewp/robot/blob/main/docs/README.md Use this command to initialize a new Astro project with the Basics starter kit. ```sh npm create astro@latest -- --template basics ``` -------------------------------- ### Create a Basic Machine with States and Context Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/createMachine.md Defines a machine with 'idle' and 'input' states and initializes context with 'first' and 'last' properties. ```javascript import { createMachine, state } from 'robot3'; const context = () => ({ first: 'Wilbur', last: 'Phillips' }); const machine = createMachine({ idle: state(), input: state() }, context); ``` -------------------------------- ### Run Core Tests Server Source: https://github.com/matthewp/robot/blob/main/contributing.md Launch the test server for the core package. Load http://localhost:1965/test/test.html in a browser to run tests. ```shell npm run --workspace=packages/core server ``` -------------------------------- ### Guard for Validation Check Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-guards.md An example of using guards for validation. This guard checks if the context contains both an email and a password before allowing the 'submit' transition. ```javascript // ✅ Good use of guards idle: state( transition('submit', 'processing', guard((ctx) => ctx.email && ctx.password) // Validation ) ) ``` -------------------------------- ### Enable Robot3 Logging Source: https://context7.com/matthewp/robot/llms.txt Import `robot3/logging` to log state transition details to the console. Each transition logs the destination state and a group containing the machine, current context, previous context, and triggering event. ```javascript import 'robot3/logging'; import { createMachine, state, transition } from 'robot3'; import { interpret } from 'robot3'; const machine = createMachine({ idle: state(transition('fetch', 'loading')), loading: state(transition('done', 'idle')) }); const service = interpret(machine, () => {}); service.send('fetch'); // Console output: // Enter state loading // ▶ Details: // Machine // Current state // Previous state // Event "fetch" ``` -------------------------------- ### Configure Test Environment for React Robot Source: https://github.com/matthewp/robot/blob/main/packages/react-robot/test/test.html Sets up the testing environment by disabling autostart for QUnit and enabling the React Act environment. Includes module imports for react, robot3, and robot-hooks. ```javascript QUnit.config.autostart = false; self.IS_REACT_ACT_ENVIRONMENT = true; { "imports": { "react": "./react.js", "robot3": "../../../node_modules/robot3/machine.js", "robot-hooks": "../../../node_modules/robot-hooks/machine.js" } } import './test.js'; QUnit.start(); ``` -------------------------------- ### Define Machine with States and Transitions Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-transitions.md Define a state machine with multiple states and transitions between them. Imports required: 'createMachine', 'state', 'transition'. ```javascript import { createMachine, state, transition } from 'robot3'; const machine = createMachine({ idle: state( transition('fetch', 'loading') ), loading: state( transition('success', 'loaded'), transition('error', 'error') ), loaded: state(), error: state( transition('retry', 'loading') ) }); ``` -------------------------------- ### Rate Limiting Guard Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-guards.md An example of a rate limiting guard that prevents actions if they occur too frequently. This guard checks the time since the last request. ```javascript const notRateLimited = (ctx) => { const now = Date.now(); const timeSinceLastRequest = now - ctx.lastRequestTime; return timeSinceLastRequest > 1000; // 1 second between requests }; const machine = createMachine({ idle: state( transition('fetch', 'loading', guard(notRateLimited) ) ), loading: state() }, () => ({ lastRequestTime: 0 })); ``` -------------------------------- ### Using `reduce` Helper for Context Updates Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-actions.md Demonstrates the `reduce` helper as a concise alternative to `action` for context-updating functions. It achieves the same result as a standard action but with less boilerplate. ```js import { reduce } from 'robot3'; // Using action const increment = action((ctx) => ({ ...ctx, count: ctx.count + 1 })); // Using reduce (same thing, less verbose) const increment = reduce((ctx) => ({ ...ctx, count: ctx.count + 1 })); // Common usage idle: state( transition('increment', 'idle', reduce((ctx) => ({ ...ctx, count: ctx.count + 1 })) ) ) ``` -------------------------------- ### Defining a Simple Logging Action Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-actions.md Shows how to define a custom action using the `action` function to log transition events. This action logs the event and returns the context unchanged. ```js import { createMachine, state, transition, action } from 'robot3'; const logTransition = action((ctx, event) => { console.log('Transitioning with:', event); return ctx; // Return unchanged context }); const machine = createMachine({ idle: state( transition('start', 'running', logTransition) ), running: state() }); ``` -------------------------------- ### Set Initial State (Default) Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-state.md By default, the first state defined in your machine configuration is set as the initial state. No explicit configuration is needed if this is desired. ```javascript const machine = createMachine({ idle: state(), // This is the initial state active: state() }); ``` -------------------------------- ### Add a Changeset for Release Automation Source: https://github.com/matthewp/robot/blob/main/contributing.md Generate a changeset file to automate the release process. Follow the prompts to describe your changes. ```shell npm run changeset ``` -------------------------------- ### API Call Action Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-actions.md An example of an action that triggers an API call to save data. It sends the current context's data to a '/api/save' endpoint using a POST request. ```js const saveToAPI = action((ctx) => { fetch('/api/save', { method: 'POST', body: JSON.stringify(ctx.data) }); return ctx; }); ``` -------------------------------- ### Basic LitElement with Robot State Machine Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/lit-robot.md Integrates a state machine into a LitElement for controlling internal state. Requires importing Robot, LitElement, and robot3's createMachine. ```js import { createMachine, state, transition } from 'robot3'; import { LitElement, html } from 'lit-element'; import { Robot } from 'lit-robot'; const machine = createMachine({ off: state( transition('toggle', 'on') ), on: state( transition('toggle', 'off') ) }); class App extends Robot(LitElement) { static machine = machine; render() { let { send } = this.service; let current = this.machine.state; return html`
State: ${current.name}
`; } } customElements.define('my-app', component(App)); ``` -------------------------------- ### Define Transitions with Target States Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-transitions.md Ensure the target state specified in a transition exists within the machine definition. This example shows transitions from 'red' to 'yellow', and 'yellow' to 'green'. ```javascript const machine = createMachine({ red: state( transition('next', 'yellow') // 'yellow' must exist ), yellow: state( transition('next', 'green') ), green: state( transition('next', 'red') ) }); ``` -------------------------------- ### Child Svelte Component Accessing Context Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/svelte-robot-factory.md A child Svelte component can access the Robot service's context to display context-specific data. This example displays the 'foo' property from the context. ```svelte /// Child.svelte
Context value of foo property: {foo}
``` -------------------------------- ### Create a Machine with an Initial State Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/createMachine.md Creates a toggle machine that can be initialized to either 'active' or 'inactive' state. The initial state is passed as the first argument to createMachine. ```javascript const toggleMachine = initial => createMachine(initial, { active: state( transition('toggle', 'inactive') ), inactive: state( transition('toggle', 'active') ) }); const myMachine = toggleMachine('inactive'); const service = interpret(myMachine, () => {}); console.log(service.machine.current, 'inactive'); ``` -------------------------------- ### Svelte Component with Robot Service Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/svelte-robot-factory.md Integrate the Robot service into a Svelte component to access its state, context, and send events. This example shows how to subscribe to the service and dispatch a 'toggle' event. ```svelte
Current state value: {current}
``` -------------------------------- ### Enable Features with Guards Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-guards.md Use guards to conditionally enable features based on context properties like user roles or feature flags. This example shows how to guard a transition to a 'newFeature' state. ```js const featureEnabled = (ctx) => ctx.features.newUI === true; const betaUser = (ctx) => ctx.user.betaTester === true; const machine = createMachine({ home: state( transition('openNewFeature', 'newFeature', guard(featureEnabled), guard(betaUser) ) ), newFeature: state() }); ``` -------------------------------- ### Chaining Multiple Actions in a Transition Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-actions.md Shows how to define a sequence of actions within a single transition. Actions are executed in the order they are listed, with the output context of one action becoming the input for the next. ```js const validate = reduce((ctx, ev) => ({ ...ctx, isValid: ev.data.length > 0 })); const trackSubmit = action((ctx) => { analytics.track('form_submitted'); return ctx; }); const clearForm = reduce((ctx) => ({ ...ctx, data: '' })); const machine = createMachine({ editing: state( transition('submit', 'submitted', validate, // 1. Validate data trackSubmit, // 2. Track event clearForm // 3. Clear form ) ), submitted: state() }); ``` -------------------------------- ### Basic Immediate Transition Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/immediate.md Defines a state that immediately transitions to the 'work' state upon entering. ```javascript import { createMachine, reduce, state, transition } from 'robot3'; const machine = createMachine({ breakfast: state( immediate('work') ), work: state() }); ``` -------------------------------- ### Use Guarded Transitions in Machine Definition Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/composition.md Define a machine using extended reusable transitions that incorporate guards. This example applies a guard to limit the 'first' name input to 10 characters. ```javascript import { createMachine, guard, state } from 'robot3'; import { formField } from './transitions.js'; const machine = createMachine({ form: state( formField('first', guard((ctx) => ctx.first.length <= 10) ), formField('last') ) }); ``` -------------------------------- ### Context Updating Actions: Increment and Set User Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-actions.md Provides examples of actions that modify the machine's context. `increment` increases a count, and `setUser` updates the user information based on event data. ```js const increment = action((ctx) => ({ ...ctx, count: ctx.count + 1 })); const setUser = action((ctx, event) => ({ ...ctx, user: event.data })); const machine = createMachine({ idle: state( transition('increment', 'idle', increment), transition('login', 'authenticated', setUser) ), authenticated: state() }, () => ({ count: 0, user: null })); ``` -------------------------------- ### Chaining Multiple Guards Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-guards.md Demonstrates how to chain multiple guards for a single transition. All guards must return true for the transition to be allowed. ```javascript const isValid = (ctx) => ctx.input.length > 0; const hasPermission = (ctx) => ctx.user.role === 'admin'; const notRateLimited = (ctx) => ctx.requestCount < 10; const machine = createMachine({ idle: state( transition('submit', 'processing', guard(isValid), guard(hasPermission), guard(notRateLimited) ) ), processing: state() }); ``` -------------------------------- ### Guard Execution Timing and Side Effects Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-guards.md Guards execute before actions and reducers, ensuring they see the old context and preventing actions/reducers from running if guards fail. This example logs context values to demonstrate execution order. ```js const machine = createMachine({ idle: state( transition('submit', 'processing', guard((ctx) => { console.log('Guard checking:', ctx.value); // Sees old value return ctx.value > 0; }), reduce((ctx) => { console.log('Reducer running:', ctx.value); // Only runs if guard passes return { ...ctx, value: ctx.value + 1 }; }) ) ), processing: state() }, () => ({ value: 5 })); ``` -------------------------------- ### Run Core Tests from CLI Source: https://github.com/matthewp/robot/blob/main/contributing.md Execute all tests for the core package directly from the command line. ```shell npm test --workspace=packages/core ``` -------------------------------- ### LitElement Component with Robot State Machine Source: https://github.com/matthewp/robot/blob/main/packages/lit-robot/readme.md Demonstrates how to create a LitElement component that integrates with Robot for state management. Ensure you have the necessary imports for Robot, LitElement, and htm. ```js import { Robot } from 'lit-robot'; import { LitElement, html } from 'lit-element'; import { html } from 'htm/prect'; class MyApp extends Robot(LitElement) { static machine = createMachine({ one: state( transition('next', 'two') ), two: state() }); render() { let { send } = this.service; let current = this.machine.state; return html` `; } } ``` -------------------------------- ### Implementing Immediate Transitions Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-events.md Use `immediate` transitions to automatically transition to a new state upon entering a specific state, often used with guards for conditional logic. ```javascript import { createMachine, state, transition, immediate } from 'robot3'; const machine = createMachine({ idle: state( transition('fetch', 'loading') ), loading: state( transition('done', 'validate') ), validate: state( // Automatically transitions when entering 'validate' immediate('loaded', guard(isValid)), immediate('error', guard(isInvalid)) ), loaded: state(), error: state() }); ``` -------------------------------- ### Initial State Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/createMachine.md Optionally, you can provide the initial state as the first argument to `createMachine`. If no initial state is provided, the first state listed in the states object will be the initial state. ```APIDOC ## createMachine(initialState, states, context?) ### Description Defines the initial state of the machine. ### Parameters #### initialState - **initialState** (string) - Required - The name of the state to be set as the initial state. #### states - **states** (object) - Required - An object where keys are state names and values are state definitions. ### Request Example ```js const toggleMachine = initial => createMachine(initial, { active: state( transition('toggle', 'inactive') ), inactive: state( transition('toggle', 'active') ) }); const myMachine = toggleMachine('inactive'); ``` ``` -------------------------------- ### Pure vs. Impure Guards Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-guards.md Illustrates the difference between pure guards (recommended) and impure guards. Pure guards do not modify context or external state and return consistent results, making them predictable and testable. ```js // ✅ Good - pure guard const isValid = (ctx) => ctx.count > 0; // ❌ Bad - has side effects const isValidWithSideEffect = (ctx) => { console.log('Checking validity'); // Side effect ctx.count++; // Mutates context return true; }; // ❌ Bad - non-deterministic const isValidRandom = (ctx) => Math.random() > 0.5; // Random result ``` -------------------------------- ### Sending Events to Robot State Machine in Haunted Component Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/haunted-robot.md This example demonstrates how to send events to a Robot state machine from a Haunted component. It uses the `send` function returned by the `useMachine` hook to trigger transitions, such as 'toggle', on button clicks. ```js const [current, send] = useMachine(machine); return html` `; ``` -------------------------------- ### Event-Driven Architecture with Multiple Event Sources Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-events.md Demonstrates how different parts of an application can send events to a state machine, making the system reactive and decoupled. Events can be simple strings or objects with data. ```javascript // Different parts of your app can send events button.addEventListener('click', () => { service.send('userAction'); }); socket.on('message', (data) => { service.send({ type: 'messageReceived', data }); }); setTimeout(() => { service.send('timeout'); }, 5000); // The machine handles all events based on current state const machine = createMachine({ waiting: state( transition('userAction', 'processing'), transition('messageReceived', 'processing'), transition('timeout', 'expired') ), processing: state(), expired: state() }); ``` -------------------------------- ### Robot Machine Definition and Service Initialization Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/svelte-robot-factory.md Define a Robot state machine with states, transitions, and context reduction. Initialize the useMachine service with the defined machine and initial event. ```javascript /// store import { createMachine, state, transition, invoke, reduce } from 'robot3'; import { useMachine } from 'svelte-robot-factory'; const context = event => ({ foo: event.foo }); const event = { foo: 'initial' }; const machine = createMachine({ inactive: state( transition('toggle', 'active', reduce((ctx, ev)=>({ ...ctx, foo: 'bar'})) ) ), active: state( transition('toggle', 'inactive', reduce((ctx, ev)=>({ ...ctx, foo: 'foo'})) ) ) }, context); const service = useMachine(machine, event); export default service; ``` -------------------------------- ### Interpret Robot3 Machines into Services Source: https://context7.com/matthewp/robot/llms.txt Create a live service from a machine using `interpret`. The service manages the machine's state, context, and provides a `send` function. It does not mutate the original machine. ```javascript import { createMachine, state, transition, reduce } from 'robot3'; import { interpret } from 'robot3'; const machine = createMachine({ asleep: state(transition('wake', 'awake')), awake: state(transition('sleep', 'asleep')) }, () => ({ wakeCount: 0 })); const service = interpret( machine, (svc) => { // onChange fires after every transition (even self-transitions) console.log(`→ ${svc.machine.current}`, svc.context); }, { wakeCount: 10 } // initialContext passed to the context function ); console.log(service.machine.current); // 'asleep' console.log(service.context.wakeCount); // 10 service.send('wake'); // → awake { wakeCount: 10 } service.send('sleep'); // → asleep { wakeCount: 10 } // Send an event object with extra data (available in guards/reducers as `ev`) service.send({ type: 'wake', source: 'alarm' }); ``` -------------------------------- ### Compose Machine with Imported Transitions Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/composition.md Import reusable transition functions from a module to create a more succinct machine definition. This promotes code reuse and cleaner machine structures. ```javascript import { createMachine, state } from 'robot3'; import { formField } from './transitions.js'; const machine = createMachine({ form: state( formField('first'), formField('last') ) }); ``` -------------------------------- ### Create a State Machine with robot3 Source: https://context7.com/matthewp/robot/llms.txt Define an immutable state machine using `createMachine`, `state`, `transition`, and `reduce`. The context function initializes the machine's context. ```javascript import { createMachine, state, transition, reduce } from 'robot3'; // Context function receives the initialContext passed to interpret() const context = initialCtx => ({ user: initialCtx?.user ?? null, count: 0, }); const machine = createMachine('idle', { idle: state( transition('start', 'running') ), running: state( transition('increment', 'running', reduce((ctx) => ({ ...ctx, count: ctx.count + 1 })) ), transition('stop', 'idle') ) }, context); console.log(machine.current); // 'idle' console.log(machine.state.name); // 'idle' console.log(machine.state.value.final); // false ``` -------------------------------- ### Define Machine Context with Initial Context Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/createMachine.md Illustrates how to define a context function that uses an initialContext argument. This context is passed to the interpret function. ```javascript const context = initialContext => ({ foo: initialContext.foo }); const machine = createMachine({ idle: state() }, context); const initialContext = { foo: 'bar' }; interpret(machine, service => { // Do stuff when the service changes. }, initialContext); ``` -------------------------------- ### Basic Preact Component with useMachine Hook Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/preact-robot.md Demonstrates a simple Preact component that uses the `useMachine` hook to manage a state machine. It displays the current state and a button to send events. ```jsx import { createMachine, state, transition } from 'robot3'; import { h, render } from 'preact'; import { useMachine } from 'preact-robot'; const machine = createMachine({ off: state( transition('toggle', 'on') ), on: state( transition('toggle', 'off') ) }); function App() { const [current, send] = useMachine(machine); return ( <>
State: {current.name}
); } render(, document.querySelector('#app')); ``` -------------------------------- ### Sending an Event to a State Machine Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-events.md Demonstrates sending an event to trigger a state transition, including guards and reducers. The `service.send('fetch')` call initiates the event flow. ```javascript const machine = createMachine({ idle: state( transition('fetch', 'loading', guard((ctx) => ctx.hasToken), reduce((ctx) => ({ ...ctx, loading: true })) ) ), loading: state() }); // This triggers the entire flow above service.send('fetch'); ``` -------------------------------- ### Astro Project Structure Overview Source: https://github.com/matthewp/robot/blob/main/docs/README.md This is a typical file structure for an Astro project. Pages are routed based on their file names in `src/pages/`. ```text / ├── public/ │ └── favicon.svg ├── src/ │ ├── components/ │ │ └── Card.astro │ ├── layouts/ │ │ └── Layout.astro │ └── pages/ │ └── index.astro └── package.json ``` -------------------------------- ### Define Machine with Context Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-state.md When creating a machine, you can provide a factory function as the second argument to define initial context data. This data persists across states. ```javascript const machine = createMachine({ idle: state( transition('fetch', 'loading') ), loading: state( transition('success', 'loaded') ), loaded: state() }, () => ({ // This is context - data that can change users: [], errorMessage: null, retryCount: 0 })); ``` -------------------------------- ### Action Pattern: Logging and Analytics Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-actions.md An action to log transition events with timestamps and track them using an analytics service. This is useful for monitoring and debugging. ```javascript const trackTransition = action((ctx, ev) => { console.log(`[${new Date().toISOString()}] ${ev.type}`); analytics.track('transition', { event: ev.type, state: ctx.currentState }); return ctx; }); // Add to every important transition transition('submit', 'processing', trackTransition) ``` -------------------------------- ### Best Practice: Single Responsibility Per Action Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-actions.md Demonstrates breaking down a complex action into smaller, focused actions, each handling a single concern like logging, persistence, or analytics. ```javascript // ❌ Bad - doing too much const megaAction = action((ctx, ev) => { console.log('Event:', ev); localStorage.setItem('data', ctx.data); analytics.track('event'); document.title = ctx.page; return { ...ctx, processed: true }; }); // ✅ Better - separate concerns const logEvent = action((ctx, ev) => { /* ... */ }); const persistData = action((ctx) => { /* ... */ }); const trackEvent = action((ctx) => { /* ... */ }); const updateTitle = action((ctx) => { /* ... */ }); const markProcessed = reduce((ctx) => ({ ...ctx, processed: true })); transition('submit', 'processing', logEvent, persistData, trackEvent, updateTitle, markProcessed ) ``` -------------------------------- ### Testing Pure Guards Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-guards.md Demonstrates how to test guards independently of the state machine. Since guards are pure functions, they can be tested with simple assertions using sample context and event objects. ```js const isEligible = (ctx, ev) => { return ev.age >= 18 && ctx.country === 'US'; }; // Test without a full machine console.assert( isEligible({ country: 'US' }, { age: 21 }) === true ); console.assert( isEligible({ country: 'US' }, { age: 16 }) === false ); console.assert( isEligible({ country: 'UK' }, { age: 21 }) === false ); ``` -------------------------------- ### State Machine With Guards Source: https://github.com/matthewp/robot/blob/main/docs/src/content/docs/concepts-guards.md Shows a concise state machine using guards to handle conditional transitions inline. This avoids excessive states. ```javascript const machine = createMachine({ idle: state( transition('submit', 'submitting', guard(isValid), guard(hasPermission) ) ), submitting: state() }); ```