New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

@servlyadmin/runtime-core

Package Overview
Dependencies
Maintainers
1
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@servlyadmin/runtime-core

Framework-agnostic core renderer for Servly components with prefetching and loading states

latest
npmnpm
Version
0.2.2
Version published
Maintainers
1
Created
Source

@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
# or
yarn add @servlyadmin/runtime-core
# or
pnpm add @servlyadmin/runtime-core

Quick Start

The simplest way to render a component:

import { mount } from '@servlyadmin/runtime-core';

// Mount a component - that's it!
const app = await mount({
  componentId: 'my-component-id',
  target: '#app',
  props: { title: 'Hello World' }
});

// Update props
app.update({ props: { title: 'Updated!' } });

// Cleanup
app.destroy();

With Callbacks

const app = await mount({
  componentId: 'my-component-id',
  target: '#app',
  props: { title: 'Hello' },
  onReady: (result) => console.log('Mounted!', result.version),
  onError: (err) => console.error('Failed:', err)
});

With Loading & Error States

const app = await mount({
  componentId: 'my-component-id',
  target: '#app',
  props: { title: 'Hello' },
  
  // Show loading indicator
  loadingComponent: '<div class="skeleton animate-pulse h-32 bg-gray-200 rounded"></div>',
  
  // Show error message on failure
  errorComponent: (err) => `<div class="text-red-500">Failed to load: ${err.message}</div>`,
  
  // Optional: delay before showing loading (avoids flash for fast loads)
  loadingDelay: 200,
  
  // Optional: minimum time to show loading (avoids flash)
  minLoadingTime: 500,
  
  // Lifecycle callbacks
  onLoadStart: () => console.log('Loading...'),
  onLoadEnd: () => console.log('Done!'),
  onReady: (result) => console.log('Mounted!'),
  onError: (err) => console.error('Failed:', err)
});

With Version & Cache Control

const app = await mount({
  componentId: 'my-component-id',
  target: document.getElementById('app'),
  props: { title: 'Hello' },
  version: '^1.0.0',
  fetchOptions: {
    cacheStrategy: 'memory',
    forceRefresh: true
  }
});

Low-Level API

For more control, use fetchComponent and render directly:

import { render, fetchComponent } from '@servlyadmin/runtime-core';

// Fetch a component from the registry
const { data } = await fetchComponent('my-component-id');

// Render to a container
const result = render({
  container: document.getElementById('app'),
  elements: data.layout,
  context: {
    props: { title: 'Hello World' },
    state: {},
    context: {},
  },
});

// Update props
result.update({
  props: { title: 'Updated Title' },
  state: {},
  context: {},
});

// Cleanup
result.destroy();

Registry Configuration

By default, components are fetched from Servly's production registry:

  • Default URL: https://core-api.servly.app/v1/views/registry

You can override this if you have a custom registry:

import { setRegistryUrl } from '@servlyadmin/runtime-core';

// Use a custom registry
setRegistryUrl('https://your-api.com/v1/views/registry');

Cache Strategies

The runtime supports three caching strategies to optimize component loading:

StrategyDescriptionPersistenceBest For
localStoragePersists across browser sessionsYesProduction apps (default)
memoryIn-memory cache, cleared on page refreshNoDevelopment, SSR
noneNo caching, always fetches freshNoTesting, debugging

Default: localStorage - Components are cached in the browser's localStorage for fast subsequent loads.

// Use default localStorage caching
const { data } = await fetchComponent('my-component');

// Explicitly set cache strategy
const { data } = await fetchComponent('my-component', {
  cacheStrategy: 'memory',  // or 'localStorage' or 'none'
});

// Force refresh (bypass cache)
const { data } = await fetchComponent('my-component', {
  forceRefresh: true,
});

Prefetching

Preload components for faster subsequent rendering:

import { prefetch, prefetchAll, prefetchOnHover, prefetchOnVisible, prefetchOnIdle } from '@servlyadmin/runtime-core';

// Prefetch a single component
await prefetch('pricing-card');

// Prefetch multiple components
await prefetchAll(['pricing-card', 'contact-form', 'navbar']);

// Prefetch with versions
await prefetchAll([
  { id: 'pricing-card', version: '^1.0.0' },
  { id: 'contact-form', version: 'latest' }
]);

// Prefetch on hover (great for modals/dialogs)
const cleanup = prefetchOnHover('#open-modal-btn', 'modal-component');
// Later: cleanup() to remove listener

// Prefetch when element becomes visible (Intersection Observer)
const cleanup = prefetchOnVisible('#pricing-section', ['pricing-card', 'feature-list']);

// Prefetch when browser is idle (non-critical components)
prefetchOnIdle(['footer', 'sidebar', 'help-modal']);

Core Concepts

Layout Elements

Components are defined as a tree of layout elements:

interface LayoutElement {
  i: string;           // Unique identifier
  componentId: string; // Element type (container, text, button, etc.)
  configuration?: {    // Element configuration
    classNames?: string;
    style?: Record<string, any>;
    text?: string;
    // ... other attributes
  };
  children?: string[]; // Child element IDs
  parent?: string;     // Parent element ID
}

Binding Context

Data is passed to components through a binding context:

interface BindingContext {
  props: Record<string, any>;   // Component props
  state: Record<string, any>;   // Component state
  context: Record<string, any>; // Additional context
}

Template Bindings

Use {{path}} syntax to bind data:

const elements = [
  {
    i: 'greeting',
    componentId: 'text',
    configuration: {
      text: 'Hello, {{props.name}}!',
      classNames: '{{props.className}}',
    },
  },
];

API Reference

The simplest way to render a component. Handles fetching, container resolution, and rendering.

const app = await mount({
  componentId: string,           // Required: Component ID to fetch
  target: string | HTMLElement,  // Required: CSS selector or element
  props?: Record<string, any>,   // Props to pass (default: {})
  state?: Record<string, any>,   // Initial state (default: {})
  context?: Record<string, any>, // Additional context (default: {})
  version?: string,              // Version specifier (default: 'latest')
  eventHandlers?: Record<string, Record<string, (e: Event) => void>>,
  fetchOptions?: {
    cacheStrategy?: 'localStorage' | 'memory' | 'none',
    forceRefresh?: boolean,
    apiKey?: string,
    retryConfig?: RetryConfig,
  },
  
  // Loading & Error States
  loadingComponent?: string | HTMLElement,  // HTML to show while loading
  errorComponent?: (err: Error) => string | HTMLElement,  // Error display
  loadingDelay?: number,         // Delay before showing loading (ms)
  minLoadingTime?: number,       // Minimum loading display time (ms)
  
  // Lifecycle Callbacks
  onLoadStart?: () => void,      // Called when loading starts
  onLoadEnd?: () => void,        // Called when loading ends
  onReady?: (result: MountResult) => void,
  onError?: (error: Error) => void,
});

// Returns MountResult
interface MountResult {
  update(context: Partial<BindingContext>): void;
  destroy(): void;
  rootElement: HTMLElement | null;
  container: HTMLElement;
  data: ComponentData;
  fromCache: boolean;
  version: string;
}

mountData(options)

Mount with pre-fetched data (useful for SSR or custom data sources):

import { mountData } from '@servlyadmin/runtime-core';

const app = mountData({
  data: myComponentData,  // Pre-fetched ComponentData
  target: '#app',
  props: { title: 'Hello' }
});

render(options)

Renders elements to a container.

const result = render({
  container: HTMLElement,
  elements: LayoutElement[],
  context: BindingContext,
  eventHandlers?: Record<string, Record<string, (e: Event) => void>>,
});

// Returns
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, version } = await fetchComponent('component-id', {
  version: 'latest',           // Version specifier (default: 'latest')
  cacheStrategy: 'localStorage', // 'localStorage' | 'memory' | 'none' (default: 'localStorage')
  forceRefresh: false,         // Bypass cache (default: false)
  retryConfig: {
    maxRetries: 3,             // Number of retry attempts (default: 3)
    initialDelay: 1000,        // Initial retry delay in ms (default: 1000)
    maxDelay: 10000,           // Maximum retry delay in ms (default: 10000)
    backoffMultiplier: 2,      // Exponential backoff multiplier (default: 2)
  },
});

setRegistryUrl(url)

Configure a custom registry URL.

import { setRegistryUrl, DEFAULT_REGISTRY_URL } from '@servlyadmin/runtime-core';

// Use custom registry
setRegistryUrl('https://your-api.com/v1/views/registry');

// Reset to default
setRegistryUrl(DEFAULT_REGISTRY_URL);

StateManager

Manages component state with subscriptions.

import { StateManager } from '@servlyadmin/runtime-core';

const stateManager = new StateManager({ count: 0 });

// Get/set state
stateManager.set('count', 1);
stateManager.get('count'); // 1
stateManager.set('user.name', 'John');

// Subscribe to changes
const unsubscribe = stateManager.subscribe((event) => {
  console.log('State changed:', event.path, event.value);
});

// Cleanup
stateManager.clear();

EventSystem

Handles events with plugin-based actions.

import { EventSystem, getEventSystem } from '@servlyadmin/runtime-core';

const eventSystem = getEventSystem();

// Register custom plugin
eventSystem.registerPlugin('my-action', async (action, context) => {
  console.log('Action executed with:', action.config);
});

Built-in Plugins

  • executeCode - Execute arbitrary JavaScript code
  • state-setState - Update state values
  • navigateTo - Navigate to URL
  • localStorage-set/get/remove - LocalStorage operations
  • sessionStorage-set/get - SessionStorage operations
  • alert - Show alert dialog
  • console-log - Log to console
  • clipboard-copy - Copy text to clipboard
  • scrollTo - Scroll to element
  • focus/blur - Focus/blur elements
  • addClass/removeClass/toggleClass - CSS class manipulation
  • setAttribute/removeAttribute - Attribute manipulation
  • dispatchEvent - Dispatch custom events
  • delay - Add delay between actions

Cache Management

import { 
  clearAllCaches, 
  clearMemoryCache, 
  clearLocalStorageCache,
  getMemoryCacheSize 
} from '@servlyadmin/runtime-core';

// Clear all caches
clearAllCaches();

// Clear specific cache
clearMemoryCache();
clearLocalStorageCache();

// Get cache size
const size = getMemoryCacheSize();

Bindings

Template resolution utilities.

import { resolveTemplate, hasTemplateSyntax } from '@servlyadmin/runtime-core';

const context = {
  props: { name: 'World', count: 42 },
  state: {},
  context: {},
};

// Resolve single template
resolveTemplate('Hello, {{props.name}}!', context); // "Hello, World!"

// Check if string has template syntax
hasTemplateSyntax('{{props.name}}'); // true
hasTemplateSyntax('static text');    // false

Prefetch API

Preload components for faster subsequent rendering.

import { 
  prefetch, 
  prefetchAll, 
  prefetchOnHover, 
  prefetchOnVisible, 
  prefetchOnIdle 
} from '@servlyadmin/runtime-core';

// Prefetch single component
await prefetch('pricing-card');
await prefetch('pricing-card', '^1.0.0');  // With version

// Prefetch multiple components
const { success, failed } = await prefetchAll([
  'pricing-card',
  'contact-form',
  { id: 'navbar', version: '^2.0.0' }
]);

// Prefetch on hover (returns cleanup function)
const cleanup = prefetchOnHover(
  '#open-modal-btn',  // Target element
  'modal-component',  // Component ID
  'latest',           // Version
  { delay: 100 }      // Options: delay before prefetch
);
// Later: cleanup()

// Prefetch when element becomes visible
const cleanup = prefetchOnVisible(
  '#pricing-section',                    // Target element
  ['pricing-card', 'feature-list'],      // Components to prefetch
  { rootMargin: '100px', threshold: 0 }  // IntersectionObserver options
);

// Prefetch during browser idle time
prefetchOnIdle(
  ['footer', 'sidebar', 'help-modal'],
  { timeout: 5000 }  // Max wait time before forcing prefetch
);

Slots

Components can define slots for content injection:

const elements = [
  {
    i: 'card',
    componentId: 'container',
    configuration: { classNames: 'card' },
    children: ['header-slot', 'content-slot'],
  },
  {
    i: 'header-slot',
    componentId: 'slot',
    configuration: {
      slotName: 'header',
    },
    parent: 'card',
  },
  {
    i: 'content-slot',
    componentId: 'slot',
    configuration: {
      slotName: 'default',
    },
    parent: 'card',
  },
];

Framework wrappers handle slot content injection automatically.

TypeScript Support

Full TypeScript support with exported types:

import type {
  LayoutElement,
  BindingContext,
  RenderResult,
  RenderOptions,
  ComponentData,
  CacheStrategy,
  RetryConfig,
  FetchOptions,
  FetchResult,
} from '@servlyadmin/runtime-core';

Browser Support

  • Chrome 80+
  • Firefox 75+
  • Safari 13+
  • Edge 80+

License

MIT

Keywords

servly

FAQs

Package last updated on 28 Dec 2025

Did you know?

Socket

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.

Install

Related posts