@servlyadmin/runtime-core
Framework-agnostic core renderer for Servly components. This package provides the foundation for rendering Servly components in any JavaScript environment.
Installation
npm install @servlyadmin/runtime-core
yarn add @servlyadmin/runtime-core
pnpm add @servlyadmin/runtime-core
Quick Start
import { render, fetchComponent } from '@servlyadmin/runtime-core';
const { data } = await fetchComponent('my-component', { version: 'latest' });
const result = render({
container: document.getElementById('app'),
elements: data.layout,
context: {
props: { title: 'Hello World' },
state: {},
context: {},
},
});
result.update({
props: { title: 'Updated Title' },
state: {},
context: {},
});
result.destroy();
Core Concepts
Layout Elements
Components are defined as a tree of layout elements:
interface LayoutElement {
i: string;
type: string;
configuration?: {
className?: string;
style?: Record<string, any>;
textContent?: string;
};
children?: LayoutElement[];
}
Binding Context
Data is passed to components through a binding context:
interface BindingContext {
props: Record<string, any>;
state: Record<string, any>;
context: Record<string, any>;
}
Template Bindings
Use {{path}} syntax to bind data:
const elements = [
{
i: 'greeting',
type: 'h1',
configuration: {
textContent: 'Hello, {{props.name}}!',
className: '{{props.className}}',
},
},
];
API Reference
render(options)
Renders elements to a container.
const result = render({
container: HTMLElement,
elements: LayoutElement[],
context: BindingContext,
eventHandlers?: Record<string, Record<string, (e: Event) => void>>,
});
interface RenderResult {
update(context: BindingContext): void;
destroy(): void;
getElement(id: string): HTMLElement | null;
}
fetchComponent(id, options?)
Fetches a component from the registry.
const { data, fromCache } = await fetchComponent('component-id', {
version: 'latest',
cacheStrategy: 'memory',
forceRefresh: false,
timeout: 30000,
retryConfig: {
maxRetries: 3,
retryDelay: 1000,
},
});
StateManager
Manages component state with subscriptions.
import { StateManager } from '@servlyadmin/runtime-core';
const stateManager = new StateManager({ count: 0 });
stateManager.setState({ count: 1 });
stateManager.getValue('count');
stateManager.setValue('user.name', 'John');
const unsubscribe = stateManager.subscribe((state) => {
console.log('State changed:', state);
});
stateManager.subscribeToPath('count', (value) => {
console.log('Count changed:', value);
});
stateManager.batch(() => {
stateManager.setValue('a', 1);
stateManager.setValue('b', 2);
});
EventSystem
Handles events with plugin-based actions.
import { EventSystem } from '@servlyadmin/runtime-core';
const eventSystem = new EventSystem();
eventSystem.registerPlugin('my-action', async (config, context) => {
console.log('Action executed with:', config);
});
const handler = eventSystem.createHandler([
{ key: 'prevent-default', config: {} },
{ key: 'set-state', config: { path: 'clicked', value: true } },
{ key: 'my-action', config: { message: 'Button clicked!' } },
]);
button.addEventListener('click', (e) => {
handler(e, 'button-id', bindingContext);
});
Built-in Plugins
console-log - Log messages to console
set-state - Update state values
delay - Add delay between actions
prevent-default - Call event.preventDefault()
stop-propagation - Call event.stopPropagation()
Cache
Component caching with multiple strategies.
import { ComponentCache } from '@servlyadmin/runtime-core';
const cache = new ComponentCache({
maxSize: 100,
strategy: 'memory',
});
cache.set('key', data, { ttl: 60000 });
cache.setComponent('comp-id', '1.0.0', componentData);
cache.getComponent('comp-id', 'latest');
cache.invalidateComponent('comp-id');
const stats = cache.getStats();
console.log(`Hit rate: ${stats.hitRate * 100}%`);
Bindings
Template resolution utilities.
import { resolveTemplate, resolveBindings, isTemplate } from '@servlyadmin/runtime-core';
const context = {
props: { name: 'World', count: 42 },
state: {},
context: {},
};
resolveTemplate('Hello, {{props.name}}!', context);
isTemplate('{{props.name}}');
isTemplate('static text');
resolveBindings({
title: '{{props.name}}',
subtitle: 'Count: {{props.count}}',
}, context);
Slots
Components can define slots for content injection:
const elements = [
{
i: 'card',
type: 'div',
configuration: { className: 'card' },
children: [
{
i: 'header-slot',
type: 'div',
configuration: {
'data-slot': 'header',
},
},
{
i: 'content-slot',
type: 'div',
configuration: {
'data-slot': 'default',
},
},
],
},
];
Framework wrappers handle slot content injection automatically.
TypeScript Support
Full TypeScript support with exported types:
import type {
LayoutElement,
BindingContext,
RenderResult,
ComponentData,
CacheStrategy,
RetryConfig,
} from '@servlyadmin/runtime-core';
Browser Support
- Chrome 80+
- Firefox 75+
- Safari 13+
- Edge 80+
License
MIT