@tirio/solid-orquest
v0.1.0
Published
Type-safe event orchestration & state management extracted from production e-commerce
Maintainers
Readme
🎯 solid-orquest
Type-Safe Event Orchestration & State Management Extracted from Production E-Commerce
Production-Proven: This library was extracted from a real-world e-commerce platform handling thousands of daily transactions. Not another theoretical state management solution.
🚀 Why solid-orquest?
Tired of state management boilerplate? solid-orquest gives you type-safe, persistent state with zero configuration and built-in event orchestration—all in under 5 minutes.
✨ What Makes Us Different
| Feature | solid-orquest | Redux | Zustand | MobX | |---------|---------------|-------|---------|------| | Type Safety | ✅ Full compile-time validation | ❌ Manual types | ⚠️ Basic | ⚠️ Decorators | | Persistence | ✅ Session/Local/Memory built-in | ❌ Middleware needed | ⚠️ Plugins | ❌ Manual | | Event Queue | ✅ Built-in rate-limited orchestration | ❌ Redux Saga/Thunk | ❌ Manual | ❌ Manual | | Zero Config | ✅ Works out of the box | ❌ Boilerplate hell | ⚠️ Some setup | ❌ Decorators | | Solid.js Native | ✅ First-class support | ❌ Adapter needed | ⚠️ Adapter | ❌ Adapter |
📦 Installation
# npm
npm install @tirio/solid-orquest
# yarn
yarn add @tirio/solid-orquest
# pnpm
pnpm add @tirio/solid-orquest🎮 60-Second Quick Start
1. Import and Use Like React Hooks (But Better)
import { useMemory, useSession, useLocal } from '@tirio/solid-orquest';
function ShoppingCart() {
// Memory: Temporary UI state (disappears on refresh)
const [count, setCount] = useMemory(0);
// Session: Survives page reloads (great for shopping carts!)
const [cart, setCart] = useSession([]);
// Local: Survives browser restarts (perfect for user preferences)
const [theme, setTheme] = useLocal('dark');
// Use them exactly like React useState hooks
const addToCart = (product) => {
setCart([...cart(), product]);
setCount(count() + 1);
};
return (
<div>
<button onClick={() => addToCart({ id: 1, name: 'Awesome Product' })}>
Add to Cart ({count()} items)
</button>
</div>
);
}2. Create Complete State Systems (Like Redux, But Simple)
import { createMemory, createSession } from '@tirio/solid-orquest';
// Create your entire application state in one place
// This is REAL code from our production e-commerce platform
const globalState = createSession([
// Basic state with getter/setter
'total', 'setTotal', [0],
'cartItems', 'setCartItems', [[]],
'customer', 'setCustomer', [{ name: '', email: '' }],
// Custom actions (like Redux reducers, but simpler)
'addToCart', [(product) => {
globalState.setCartItems([...globalState.cartItems(), product]);
globalState.setTotal(globalState.total() + product.price);
}],
'clearCart', [() => {
globalState.setCartItems([]);
globalState.setTotal(0);
}]
] as const); // ← "as const" gives you FULL TypeScript inference!
// TypeScript knows EVERYTHING about your state:
globalState.addToCart({ id: 1, name: 'Product', price: 29.99 }); // ✅ Perfectly typed
globalState.addToCart(123); // ❌ TypeScript error: wrong argument type
globalState.nonexistentMethod(); // ❌ TypeScript error: method doesn't exist🏗️ Real-World Production Architecture
Here's exactly how we structure enterprise applications with solid-orquest in our production e-commerce platform:
Step 1: Define Your Global State
// src/feats/globalState/GlobalContext.ts
import { createMemory, createSession } from '@tirio/solid-orquest';
import { PublicItem, Customer, Shopman } from '@/feats/types';
import { SETTINGS } from '@/feats/settings';
export const globalState = () => ({
// Session storage: Survives page reloads
...createSession([
'total', 'setTotal', [0],
'total2', 'setTotal2', [0],
'publicItems', 'setPublicItems', [[] as PublicItem[]],
'paymentMethod', 'setPaymentMethod', ['PUE'],
'paymentWay', 'setPaymentWay', ['01'],
'destiny', 'setDestiny', [''],
'coin', 'setCoin', ['MXN'],
'cfdiUse', 'setCfdiUse', [''],
'customer', '_setCustomer', [{ ...SETTINGS.DEFAULT_CUSTOMER }]
] as const),
// Memory storage: Temporary UI state
...createMemory([
'itemsFound', 'setItemsFound', [[] as PublicItem[]],
'customersFound', 'setCustomersFound', [[] as Customer[]],
'usersFound', 'setUsersFound', [[] as Shopman[]],
'searchResultSet', 'setSearchResultSet', [[]],
'requestNumber', 'setRequestNumber', [0],
'isPortableDevice', 'setIsPortableDevice', [SETTINGS.PORTABLE],
'discount', 'setDiscount', [0],
// Business logic methods
'setCustomer', [(customer: Customer) => {
global.setCfdiUse(customer.cfdiUse);
global._setCustomer(customer);
}],
'updateTotals', [() => {
const items = global.publicItems();
let total = 0;
let total2 = 0;
items.map(item => {
if (!item.disabled) {
total2 += item.quantity * item.unitPriceComputed;
}
total += item.quantity * item.unitPriceComputed;
});
global.setTotal(round2(total));
global.setTotal2(round2(total2));
}],
'addItem', [(item: PublicItem) => {
global.setPublicItems(items => {
item.quantity = getCommandControlState().commandLine().quantity! || item.quantity;
if (item.edited)
item.unitPriceComputed = item.unitPrice;
else
item.unitPriceComputed = round2(item.unitPrice - (item.unitPrice - item.providerPrice) * (global.discount() / 100));
items.unshift(item);
return items;
});
global.updateTotals();
}],
'recomputeSelectedItems', [() => {
global.setPublicItems(items => {
for (let i = 0; i < items.length; i++) {
if (!items[i].edited)
items[i].unitPriceComputed = round2(items[i].unitPrice - (items[i].unitPrice - items[i].providerPrice) * (global.discount() / 100));
}
return items;
});
}],
'idleTimer', 'setIdleTimer', [{} as IdleTimer],
] as const),
});
export type GlobalContext = ReturnType<typeof globalState>;Step 2: Create a Context Provider
// src/feats/globalState/GlobalProvider.tsx
import { GlobalContext, globalState } from './GlobalContext';
import { ChildrenProps } from '@/feats/types';
import { useContext } from 'solid-js';
export const GlobalProvider = (props: ChildrenProps) => {
const global = globalState();
const provider = (
<GlobalContext.Provider value={global}>
{props.children}
</GlobalContext.Provider>
);
return provider;
};
export function useGlobal() {
return useContext(GlobalContext)!;
}Step 3: Use in Components (Clean & Simple)
// src/feats/components/ShoppingCart.tsx (simplified)
import { useGlobal } from '@/feats/globalState';
export function ShoppingCart() {
const global = useGlobal();
return (
<div>
<h2>Your Cart ({global.publicItems().length} items)</h2>
<p>Total: ${global.total()}</p>
<button onClick={() => global.addItem(newProduct)}>
Add Product
</button>
</div>
);
}📚 Complete Tutorial: Build a Todo App in 5 Minutes
Let's build a fully-featured todo app with persistence, type safety, and clean architecture:
1. Setup State
// src/state/todoState.ts
import { createSession, createMemory } from '@tirio/solid-orquest';
export const todoState = () => ({
...createSession([
'todos', 'setTodos', [[]],
'filter', 'setFilter', ['all']
] as const),
...createMemory([
'newTodoText', 'setNewTodoText', [''],
'addTodo', [() => {
const text = todo.newTodoText().trim();
if (text) {
const newTodo = {
id: Date.now(),
text,
completed: false,
createdAt: new Date()
};
todo.setTodos([...todo.todos(), newTodo]);
todo.setNewTodoText('');
}
}],
'toggleTodo', [(id: number) => {
todo.setTodos(
todo.todos().map(t =>
t.id === id ? { ...t, completed: !t.completed } : t
)
);
}],
'clearCompleted', [() => {
todo.setTodos(todo.todos().filter(t => !t.completed));
}],
'filteredTodos', [() => {
const todos = todo.todos();
const filter = todo.filter();
switch (filter) {
case 'active': return todos.filter(t => !t.completed);
case 'completed': return todos.filter(t => t.completed);
default: return todos;
}
}]
] as const),
});2. Create Provider
// src/state/TodoProvider.tsx
import { createContext, useContext } from 'solid-js';
import { todoState } from './todoState';
const TodoContext = createContext<ReturnType<typeof todoState>>();
export function TodoProvider({ children }: { children: any }) {
const state = todoState();
return (
<TodoContext.Provider value={state}>
{children}
</TodoContext.Provider>
);
}
export function useTodo() {
return useContext(TodoContext)!;
}3. Build Components
// src/components/TodoApp.tsx
import { useTodo } from '../state/TodoProvider';
export function TodoApp() {
const todo = useTodo();
return (
<div>
<h1>Todo App ({todo.filteredTodos().length} tasks)</h1>
<input
value={todo.newTodoText()}
onInput={(e) => todo.setNewTodoText(e.currentTarget.value)}
onKeyPress={(e) => e.key === 'Enter' && todo.addTodo()}
placeholder="What needs to be done?"
/>
<button onClick={() => todo.addTodo()}>Add</button>
<div>
<button onClick={() => todo.setFilter('all')}>All</button>
<button onClick={() => todo.setFilter('active')}>Active</button>
<button onClick={() => todo.setFilter('completed')}>Completed</button>
<button onClick={() => todo.clearCompleted()}>Clear Completed</button>
</div>
<ul>
{todo.filteredTodos().map(item => (
<li key={item.id}>
<input
type="checkbox"
checked={item.completed}
onChange={() => todo.toggleTodo(item.id)}
/>
<span style={{ textDecoration: item.completed ? 'line-through' : 'none' }}>
{item.text}
</span>
</li>
))}
</ul>
</div>
);
}4. Profit! 🎉
Your todo app now has:
- ✅ Persistence: Todos survive page reloads (session storage)
- ✅ Type Safety: Full TypeScript support
- ✅ Clean Architecture: Separation of concerns
- ✅ Zero Boilerplate: No actions, reducers, or dispatchers
- ✅ Built-in Logic: Filtering computed from state
🎯 Advanced Features You'll Actually Use
1. Built-in Event Queue (Forget Redux Thunk/Saga)
import { Events } from '@tirio/solid-orquest';
const events = new Events();
// Register async operations
events.register(async (userId) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}, 'fetchUser');
events.register(async (orderData) => {
// Complex workflow with multiple steps
await events.queue('validateOrder', orderData);
await events.queue('processPayment', orderData);
await events.queue('sendConfirmation', orderData);
return { success: true, orderId: '123' };
}, 'createOrder');
// Use in your components
async function handleCheckout() {
// All async, all queued, all manageable
const user = await events.queue('fetchUser', currentUserId);
const order = await events.queue('createOrder', orderData);
// Built-in rate limiting (50 calls/second default)
// Built-in error handling
// Built-in parent-child task relationships
}2. Smart Storage That Just Works
import { useStore } from '@tirio/solid-orquest';
// Manual storage control when you need it
const [getCart, setCart] = useStore(
[], // Initial value
0, // Optional: Custom key ID
'sessionStorage' // 'localStorage' | 'memoryStorage'
);
// Everything is automatically:
// ✅ Serialized to JSON
// ✅ Encoded for safety
// ✅ Type-safe on retrieval
// ✅ Error-handled
// Plus: Stack trace fingerprinting for debugging
// Each storage operation gets a unique hash based on call location
// Debugging storage issues? We show you EXACTLY where it was called from3. Production-Ready Debugging
// In development, expose state to browser console
if (!import.meta.env.PROD) {
// Type `globalState` in console to explore everything
// Type `Events.LOGS` to see the event queue history
// Every storage operation is logged with stack traces
}
// Built-in performance monitoring
import { setMaxTasksPerSecond } from '@tirio/solid-orquest';
// Adjust based on your app's needs
setMaxTasksPerSecond(100); // Default: 50🔧 API Reference (Cheat Sheet)
Quick Functions
// Basic hooks
const [value, setValue] = useMemory(initial); // Temp state
const [value, setValue] = useSession(initial); // Session persistence
const [value, setValue] = useLocal(initial); // Local persistence
// Complete state objects
const state = createMemory([...]); // Memory-backed
const state = createSession([...]); // Session-backed
const state = createLocal([...]); // Local-backed
// Event system
const events = new Events();
events.queue('taskName', ...args); // Queue task
events.register(fn, 'taskName'); // Register task
// Configuration
setMaxTasksPerSecond(100); // Adjust rate limitArray Format Explained
createMemory([
'getterName', 'setterName', [initialValue], // Getter/setter pair
'methodName', [(arg1, arg2) => { ... }], // Custom method
'computedName', [() => { return computed; }] // Computed value
] as const);📖 Migration Guide
Coming from Redux?
// BEFORE: Redux boilerplate hell
const initialState = { count: 0 };
const reducer = (state, action) => { /* 50 lines */ };
const store = createStore(reducer);
const dispatch = useDispatch();
dispatch({ type: 'INCREMENT' });
// AFTER: Clean and simple
const [count, setCount] = useSession(0);
setCount(count() + 1); // Done!Coming from Zustand?
// BEFORE: Zustand
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));
// AFTER: Built-in persistence and types
const [count, setCount] = useSession(0);
const increment = () => setCount(count() + 1);Coming from MobX?
// BEFORE: MobX with decorators
class Store {
@observable count = 0;
@action increment() { this.count++; }
}
// AFTER: No decorators, same reactivity
const [count, setCount] = useMemory(0);
const increment = () => setCount(count() + 1);🤔 Frequently Asked Questions
Q: Is this only for Solid.js?
A: While built with Solid.js in mind, solid-orquest works with any framework (React, Vue, Angular) or vanilla JavaScript. The event system and storage are framework-agnostic.
Q: How does persistence work?
A: We automatically serialize to JSON and handle all the edge cases (circular references, Dates, undefined values). Choose sessionStorage for temporary persistence or localStorage for permanent storage.
Q: What about server-side rendering (SSR)?
A: The storage adapters automatically detect SSR environments and fall back to memory storage. No configuration needed.
Q: Is there a size limit?
A: We respect browser storage limits (usually 5-10MB). The event queue has built-in rate limiting to prevent memory issues.
Q: Can I use this with TypeScript?
A: That's our superpower! We use advanced TypeScript features to give you full compile-time safety. Every method, every parameter, every return type is checked.
🐛 Debugging & Support
Quick Debugging
// 1. Console access (dev only)
(window as any).globalState // Explore your entire state
// 2. Event logs
console.log(Events.LOGS); // See every queued task
// 3. Storage inspection
console.log(sessionStorage); // Direct browser storage accessGetting Help
- Documentation: See INTERNALS.md for deep technical details
- Issues: Open a GitHub issue for bugs or questions
- Contributions: PRs welcome! See our development plan in WORK_PLAN.md
📄 License
MIT © Tirio
🚀 Ready to Simplify Your State Management?
npm install @tirio/solid-orquestJoin developers who've eliminated state management complexity and shipped faster with confidence.
"We reduced our state management code by 70% and eliminated entire classes of bugs with solid-orquest's type system."
— Real feedback from our production team
Happy coding! 🎉
