@legible-sync/core
v1.3.1
Published
Core framework for LegibleSync - What You See Is What It Does
Maintainers
Readme
@legible-sync/core
Core framework for LegibleSync - implementing the "What You See Is What It Does" architectural pattern.
Installation
npm install @legible-sync/coreQuick Start
import { LegibleEngine, Concept } from '@legible-sync/core';
// Define a concept
class UserConcept implements Concept {
name = 'User';
state = { users: new Map() };
async execute(action: string, input: any) {
if (action === 'register') {
// Implementation
return { userId: '123' };
}
}
}
// Create engine and register concept
const engine = new LegibleEngine();
engine.registerConcept(new UserConcept());
// Execute actions
const result = await engine.invoke('User', 'register', {
username: 'alice',
email: '[email protected]'
}, 'flow-1');
console.log(result); // { userId: '123' }Architecture
Concepts
Independent modules that encapsulate state and behavior:
interface Concept {
name: string;
state: Record<string, any>;
execute(action: string, input: any): Promise<any>;
rollback?(action: string, input: any, output: any): Promise<void>; // Optional for rollback support
}Synchronizations
Declarative rules that define when concepts interact:
interface SyncRule {
name: string;
when: Pattern[];
where?: Query;
then: Invocation[];
}Rollback on Failure
If a sync's then action fails, the engine automatically rolls back successful previous actions in the same sync by calling their rollback method (if implemented). This ensures atomicity within sync executions.
interface Concept {
name: string;
state: Record<string, any>;
execute(action: string, input: any): Promise<any>;
rollback?(action: string, input: any, output: any): Promise<void>; // Optional rollback
}Example: If a sync has three then actions and the second fails, the first is rolled back, and the third is not executed.
Query Example
const userFilterSync: SyncRule = {
name: "PremiumUserNotification",
when: [
{
concept: "User",
action: "register"
}
],
where: {
filter: (bindings) => {
// Filter bindings before executing then clauses
const email = bindings.email as string;
return email?.endsWith('@premium.com') || false;
}
},
then: [
{
concept: "Email",
action: "send",
input: { to: "?email", subject: "Welcome Premium User!" }
}
]
};Engine
Runtime that orchestrates concept execution and synchronization triggering.
Use Cases and Best Practices
Core Business Logic with Syncs
Use syncs for primary business flows like user registration or order processing. They ensure synchronous, traceable interactions between concepts.
Side Effects and Decoupling
For asynchronous side effects (notifications, analytics), consider integrating an event system. While syncs can trigger these, an external EventBus provides better decoupling and resilience. See the example-eda for an event-driven implementation.
General Guidelines
- Keep syncs focused on business logic to maintain legibility.
- Test syncs thoroughly due to their declarative nature.
- For complex systems, combine syncs with event-driven patterns.
API Reference
LegibleEngine
registerConcept(concept: Concept)
Register a concept with the engine.
registerSync(sync: SyncRule)
Register a synchronization rule.
invoke(concept: string, action: string, input: any, flowId: string)
Execute an action on a concept within a flow.
getFlowActions(flowId: string)
Get all actions executed in a flow for auditing.
Research Foundation
This framework implements the research from:
"What You See Is What It Does: A Structural Pattern for Legible Software"
Eagon Meng, Daniel Jackson
License
MIT License - Copyright (c) 2025 Mauro Stepanoski
