npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@servlyadmin/runtime-core

v0.1.46

Published

Framework-agnostic core renderer for Servly components

Readme

@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 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:

| Strategy | Description | Persistence | Best For | |----------|-------------|-------------|----------| | localStorage | Persists across browser sessions | Yes | Production apps (default) | | memory | In-memory cache, cleared on page refresh | No | Development, SSR | | none | No caching, always fetches fresh | No | Testing, 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,
});

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

mount(options) - Recommended

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,
  },
  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

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