
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
equilibria-engine
Advanced tools
A framework-agnostic TypeScript library for creating dynamic economic charts with Redux state management and real-time interactivity.
Version 1.4.1
A framework-agnostic JavaScript library for creating interactive economic visualizations, built on Redux, D3.js, and Math.js. Features a modular architecture with advanced performance optimizations for smooth, responsive charts.
For Developers: See the AI Context Documentation for detailed architecture and implementation guides.
pnpm add equilibria-engine
npm install equilibria-engine
yarn add equilibria-engine
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Supply & Demand</title>
<link rel="stylesheet" href="node_modules/equilibria-engine/src/styles/engine.css">
</head>
<body>
<div id="app">
<div id="chart-container"></div>
<div id="controls"></div>
</div>
<script type="module">
import { initEngine, parseYAML, initializeFromYAML } from 'equilibria-engine';
// Initialize the engine
const { store, destroy } = initEngine({
chartContainerId: 'chart-container',
controlsHost: document.getElementById('controls')
});
// Load a model from YAML
fetch('models/supply-demand.yaml')
.then(res => res.text())
.then(yaml => {
const parsed = parseYAML(yaml);
store.dispatch(initializeFromYAML(parsed));
console.log('Model loaded!');
});
</script>
</body>
</html>
# supply-demand.yaml
metadata:
title: "Market for Coffee"
specVersion: "1.0"
parameters:
demandIntercept:
value: 100
min: 50
max: 150
label: "Demand Intercept"
supplyIntercept:
value: 20
min: 0
max: 50
label: "Supply Intercept"
slope:
value: 2
min: 0.5
max: 5
step: 0.5
charts:
- id: mainChart
title: "Coffee Market"
xAxis:
label: "Quantity"
min: 0
max: 60
yAxis:
label: "Price ($)"
min: 0
max: 120
elements:
- id: demand
type: line
label: "Demand"
equation: "demandIntercept - slope * x"
color: "#2563eb"
draggable: true
- id: supply
type: line
label: "Supply"
equation: "supplyIntercept + slope * x"
color: "#dc2626"
Equilibria uses Redux for predictable state management:
import { createStore, getState, dispatch } from 'equilibria-engine';
// Create store with initial state
const store = createStore(initialState);
// Get current state
const state = store.getState();
// Dispatch actions
import { setParameter, activateScenario } from 'equilibria-engine';
store.dispatch(setParameter('demandIntercept', 120));
store.dispatch(activateScenario('imposeTax'));
Generate shareable links that preserve chart state:
import { serializeState, deserializeState } from 'equilibria-engine';
// Save state to URL
const compressed = serializeState(store.getState());
const url = `${window.location.origin}?state=${compressed}`;
// Restore state from URL
const params = new URLSearchParams(window.location.search);
const compressed = params.get('state');
if (compressed) {
const state = deserializeState(compressed);
store = createStore(state);
}
The engine features a modular architecture with 14 focused modules for maintainability and performance:
src/engine/
├── rendering/
│ ├── core.ts # Main orchestration
│ ├── ChartRenderer.ts # Per-chart rendering class
│ ├── scales.ts # D3 scale calculation
│ ├── axes.ts # Axes rendering
│ ├── gridlines.ts # Gridlines rendering
│ ├── initialization.ts # Chart setup
│ ├── elements/
│ │ ├── lines.ts # Line rendering
│ │ ├── areas.ts # Area rendering
│ │ ├── points.ts # Point rendering
│ │ └── annotations.ts # Text annotations
│ ├── interactions/
│ │ └── drag.ts # Drag behaviors
│ ├── math/
│ │ ├── evaluator.ts # Expression evaluation (with memoization)
│ │ └── intersections.ts # Curve intersections
│ └── types.ts # TypeScript definitions
├── cache/
│ └── memoization.ts # LRU cache infrastructure
├── chartSubscriptions.ts # Selective rendering
├── reducer.ts # Redux reducer
└── store.ts # Redux store
1. Expression Memoization
2. Selective Rendering
3. Modular Design
| Operation | Before | After | Improvement |
|---|---|---|---|
| Parameter update | ~16ms | ~8ms | 2x faster |
| Chart re-render | ~60ms | ~30ms | 2x faster |
| 4-chart dashboard | ~120ms | ~40ms | 3x faster |
| 1000 evaluations | ~45ms | ~8ms | 5.5x faster |
initializeEngine(options)Initialize the Equilibria engine with configuration options.
Parameters:
options.chartContainer (string): CSS selector for chart containeroptions.controlsContainer (string): CSS selector for controls containeroptions.initialState (object, optional): Initial Redux stateReturns: Engine instance with methods: loadYAML(), getState(), dispatch()
serializeState(state)Compress and encode Redux state for URL sharing.
Parameters:
state (object): Redux state objectReturns: Base64-encoded compressed string
deserializeState(compressed)Decompress and decode state from URL.
Parameters:
compressed (string): Base64-encoded compressed stateReturns: Redux state object
import {
setParameter,
setMultipleParameters,
shiftCurve,
toggleElementVisibility,
activateScenario,
deactivateScenario
} from 'equilibria-engine';
// Set parameter value
dispatch(setParameter('price', 100));
// Batch update parameters
dispatch(setMultipleParameters({
tax: 10,
subsidy: 0
}));
// Shift a curve element
dispatch(shiftCurve('demand', { yShift: 10 }));
// Toggle element visibility
dispatch(toggleElementVisibility('supply'));
// Activate scenario
dispatch(activateScenario('imposeTax'));
// Deactivate scenario
dispatch(deactivateScenario('imposeTax'));
The Equilibria Engine is designed to be framework-agnostic and easy to integrate into any JavaScript application.
import { initEngine } from 'equilibria-engine';
// 1. Initialize the engine
const { store, destroy } = initEngine({
chartContainerId: 'my-chart-container',
controlsHost: document.getElementById('controls'),
initialState: null, // or load from YAML
renderConfig: {
width: 800,
height: 600,
margin: { top: 20, right: 20, bottom: 40, left: 50 }
},
useSelectiveRendering: true // Enable for multi-chart performance
});
// 2. Load a model
fetch('/models/my-model.yaml')
.then(res => res.text())
.then(yaml => {
const parsed = parseYAML(yaml);
store.dispatch(initializeFromYAML(parsed));
});
// 3. Clean up when done
window.addEventListener('beforeunload', () => {
destroy();
});
import { useEffect, useRef } from 'react';
import { initEngine, parseYAML, initializeFromYAML } from 'equilibria-engine';
function EconomicChart({ modelYaml }) {
const chartRef = useRef(null);
const controlsRef = useRef(null);
const engineRef = useRef(null);
useEffect(() => {
// Initialize engine
const { store, destroy } = initEngine({
chartContainerId: chartRef.current,
controlsHost: controlsRef.current,
useSelectiveRendering: true
});
engineRef.current = { store, destroy };
// Load model
if (modelYaml) {
const parsed = parseYAML(modelYaml);
store.dispatch(initializeFromYAML(parsed));
}
// Cleanup
return () => destroy();
}, [modelYaml]);
return (
<div>
<div ref={chartRef} className="chart-container" />
<div ref={controlsRef} className="controls-container" />
</div>
);
}
<template>
<div>
<div ref="chartContainer" class="chart-container"></div>
<div ref="controlsContainer" class="controls-container"></div>
</div>
</template>
<script>
import { initEngine, parseYAML, initializeFromYAML } from 'equilibria-engine';
export default {
props: ['modelYaml'],
mounted() {
const { store, destroy } = initEngine({
chartContainerId: this.$refs.chartContainer,
controlsHost: this.$refs.controlsContainer,
useSelectiveRendering: true
});
this.engine = { store, destroy };
if (this.modelYaml) {
const parsed = parseYAML(this.modelYaml);
store.dispatch(initializeFromYAML(parsed));
}
},
beforeUnmount() {
this.engine?.destroy();
}
};
</script>
<script>
import { onMount, onDestroy } from 'svelte';
import { initEngine, parseYAML, initializeFromYAML } from 'equilibria-engine';
export let modelYaml;
let chartContainer;
let controlsContainer;
let engine;
onMount(() => {
const { store, destroy } = initEngine({
chartContainerId: chartContainer,
controlsHost: controlsContainer,
useSelectiveRendering: true
});
engine = { store, destroy };
if (modelYaml) {
const parsed = parseYAML(modelYaml);
store.dispatch(initializeFromYAML(parsed));
}
});
onDestroy(() => {
engine?.destroy();
});
</script>
<div bind:this={chartContainer} class="chart-container"></div>
<div bind:this={controlsContainer} class="controls-container"></div>
1. Use Selective Rendering for Multi-Chart Dashboards
const { store, destroy } = initEngine({
chartContainerId: 'container',
controlsHost: controls,
useSelectiveRendering: true // Only re-render affected charts
});
2. Batch Parameter Updates
import { setMultipleParameters } from 'equilibria-engine';
// ❌ Bad - triggers multiple re-renders
dispatch(setParameter('tax', 10));
dispatch(setParameter('subsidy', 5));
dispatch(setParameter('quota', 100));
// ✅ Good - single re-render
dispatch(setMultipleParameters({
tax: 10,
subsidy: 5,
quota: 100
}));
3. Monitor Cache Performance
import { getCacheStats } from 'equilibria-engine';
// Check cache hit rates
const stats = getCacheStats();
console.log('Expression cache:', stats.compiledExpressions);
console.log('Evaluation cache:', stats.evaluations);
console.log('Hit rate:', (stats.evaluations.utilization * 100).toFixed(1) + '%');
4. Clear Caches on Model Changes
import { clearEvaluationCaches } from 'equilibria-engine';
// Clear caches when loading a completely new model
function loadNewModel(yaml) {
clearEvaluationCaches();
const parsed = parseYAML(yaml);
store.dispatch(initializeFromYAML(parsed));
}
Subscribe to Specific Changes
let previousParameters = null;
store.subscribe(() => {
const state = store.getState();
// Only react to parameter changes
if (state.parameters !== previousParameters) {
previousParameters = state.parameters;
console.log('Parameters changed:', state.parameters);
// Update external UI, analytics, etc.
}
});
Integrate with External State Management
// Redux Toolkit example
import { createSlice } from '@reduxjs/toolkit';
const economicsSlice = createSlice({
name: 'economics',
initialState: { equilibriaState: null },
reducers: {
syncEquilibriaState: (state, action) => {
state.equilibriaState = action.payload;
}
}
});
// Sync Equilibria state to your app's Redux store
equilibriaStore.subscribe(() => {
const eqState = equilibriaStore.getState();
appStore.dispatch(syncEquilibriaState(eqState));
});
Extend Math.js with domain-specific functions:
import { math } from 'equilibria-engine';
math.import({
elasticity: function(price, quantity, dP, dQ) {
return (dQ / quantity) / (dP / price);
}
});
// Use in YAML equations
// equation: "elasticity(price, quantity, 1, -2)"
Listen to state changes:
store.subscribe(() => {
const state = store.getState();
console.log('State updated:', state);
// Custom logic based on state
if (state.parameters.price.value > 100) {
console.log('Price is high!');
}
});
import { render, ChartRenderer } from 'equilibria-engine';
// Render all charts
render('chart-container', store.getState(), config, store);
// Use ChartRenderer for per-chart control
const renderer = new ChartRenderer('chart-container', config, store);
renderer.renderChart('mainChart', chartDef, store.getState());
parameters:
tax:
value: 0
min: 0
max: 30
step: 5
label: "Tax ($/unit)"
charts:
- id: mainChart
elements:
- id: supply
type: line
equation: "20 + 2 * x + tax" # Tax shifts supply up
color: "#dc2626"
- id: consumerSurplus
type: area
topBoundary: "demand"
bottomBoundary: "equilibriumPrice"
leftBoundary: "0"
rightBoundary: "equilibriumQuantity"
color: "#10b981"
opacity: 0.3
parameters:
alpha:
value: 0.5
min: 0
max: 1
step: 0.1
label: "Technology Parameter"
charts:
- id: ppf
title: "Production Possibilities Frontier"
elements:
- id: frontier
type: line
equation: "100 * (1 - (x / 100) ** alpha)"
color: "#2563eb"
Brackets visualize ranges and changes on axes (ΔQ, ΔP, tax wedges).
parameters:
q1:
value: 30
label: "Initial Quantity"
q2:
value: 50
label: "Final Quantity"
charts:
- id: mainChart
elements:
- id: quantityChange
type: bracket
axis: 'x'
from: "q1"
to: "q2"
label: 'ΔQ'
offset: -20
color: "#6366f1"
- id: priceChange
type: bracket
axis: 'y'
from: 40
to: 60
label: 'ΔP'
offset: 20
color: "#ec4899"
Properties:
type: 'bracket' - Element typeaxis: 'x' | 'y' - Which axis to place bracket onfrom: number | string - Start value (parameter reference allowed)to: number | string - End value (parameter reference allowed)label?: string - Text label for the bracketoffset?: number - Distance from axis (negative = below/left, positive = above/right)color?: string - Bracket colorUse Cases:
Sliders provide direct on-chart parameter manipulation.
parameters:
tax:
value: 0
min: 0
max: 30
charts:
- id: interactive
elements:
- id: taxSlider
type: slider
param: "tax"
axis: 'y'
min: 0
max: 30
label: "Tax"
format: "$,.0f"
color: "#8b5cf6"
position: 10 # Position on perpendicular axis
Properties:
type: 'slider' - Element typeparam: string - Parameter name to controlaxis: 'x' | 'y' - Which axis to place slider handle onmin: number | string - Minimum valuemax: number | string - Maximum valuelabel?: string - Display labelformat?: string - D3 format string for value display (e.g., "$,.0f", ".2f")color?: string - Handle and track colorposition?: number | string - Position on perpendicular axisUse Cases:
Equilibria includes JSON Schema export for enhanced IDE support and YAML validation.
Export JSON Schema v7 from Zod schemas:
# One-time export
pnpm schema:export
# Output: schema/equilibria-document.schema.json (~41KB)
1. Install Extension
Install YAML by Red Hat from the VSCode marketplace.
2. Schema Auto-Configuration
The .vscode/settings.json file is pre-configured to map the schema to your YAML files:
{
"yaml.schemas": {
"./schema/equilibria-document.schema.json": [
"*.equilibria.yaml",
"examples/**/*.yaml",
"tests/fixtures/**/*.yaml"
]
}
}
3. IDE Features
Once configured, you get:
Ctrl+Space for field suggestionsspecVersion, element types, etc.Example YAML with IDE Support:
# Type "met" and press Ctrl+Space to autocomplete "metadata:"
metadata:
title: "My Economic Model"
specVersion: "1.0" # ← Autocomplete suggests valid versions
# Type "param" and press Ctrl+Space
parameters:
price:
value: 100
min: 0 # ← Hover to see "Minimum allowed value (number)"
max: 200
label: "Market Price"
# Type "chart" and see the array structure suggested
charts:
- id: "mainChart"
elements:
- type: "line" # ← Autocomplete suggests: line, area, point, verticalLine, etc.
equation: "100 - 2 * x"
color: "#2563eb"
$refs)x-equilibria-version and x-generated-from fieldssrc/schemas/If you modify the Zod schemas, regenerate the JSON Schema:
pnpm schema:export
The engine includes comprehensive test coverage:
# Run all tests
pnpm test
# Watch mode
pnpm test:watch
# Coverage report
pnpm test:coverage
# E2E tests
pnpm test:e2e
Test Stats:
Equilibria is designed with accessibility as a core requirement:
Tab - Navigate between controlsArrow Up/Down - Adjust slider valuesEnter - Activate buttonsSpace - Toggle checkboxesEscape - Close dialogsOptimization Tips:
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Quick Start for Contributors:
# Clone repo
git clone https://github.com/your-org/equilibria.git
# Install dependencies (from monorepo root)
pnpm install
# Run tests
cd packages/equilibria-engine
pnpm test
# Make changes and test
pnpm test:watch
See CHANGELOG.md for version history and breaking changes.
MIT © 2025 Kinetonomics
You can load the built bundles directly from popular CDNs (jsDelivr / unpkg). These files are produced in dist/ during the package build.
UMD (script tag) — jsDelivr / unpkg (pinned to v1.0.2)
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/equilibria-engine@1.0.2/dist/equilibria-engine.umd.min.js"></script>
<!-- or unpkg -->
<script src="https://unpkg.com/equilibria-engine@1.0.2/dist/equilibria-engine.umd.min.js"></script>
The UMD build exposes a global. Common names to try are EquilibriaEngine or equilibriaEngine (the exact global depends on the build name in Rollup). Example usage:
<script>
// The UMD bundle attaches to window
const { initializeEngine } = window.EquilibriaEngine || window.equilibriaEngine || {};
const engine = initializeEngine({ chartContainer: '#chart', controlsContainer: null });
// engine.loadYAML(...)
</script>
ESM (native module import) — esm.sh (pinned to v1.0.2)
<script type="module">
// For reliable named ESM exports in browser demos, use the esm.sh pre-bundled entry.
import { initializeEngine } from 'https://esm.sh/equilibria-engine@1.0.2/dist/equilibria-engine.esm.js?bundle';
const engine = initializeEngine({ chartContainer: '#chart' });
// engine.loadYAML(...)
</script>
Built with:
FAQs
A framework-agnostic TypeScript library for creating dynamic economic charts with Redux state management and real-time interactivity.
We found that equilibria-engine demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.