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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jamplus/kronos

v1.0.1

Published

Designer initialization package for Olympus.

Readme

Kronos

Kronos is a lightweight iframe and postMessage manager for embedding a JAM+ designer into any web page. It handles iframe creation, sandboxed module loading, and a typed async RPC + event bus between your page and the designer — no build tooling or framework required on the integrator side.

How it works

createDesigner injects an iframe into a container element you provide. It either loads the designer from a URL directly, or bootstraps a designer JS module into a blob iframe (handling Vite HMR stubs, base URL resolution, and method auto-registration). Once the designer signals ready, you get back a DesignerInstance handle.

All communication between your page and the designer goes through postMessage. Kronos manages call IDs, pending response queues, and event handlers transparently.


Installation

You can pull the latest Kronos ESM module from NPM

npm install @jamplus/kronos
import DesignerManager from '@jamplus/kronos';

API

createDesigner(options): Promise<DesignerInstance>

Creates the iframe and returns a handle once the designer is ready.

type DesignerOptions = {
  container: HTMLElement;   // element to append the iframe into
  url?: string;             // load a prebuilt designer iframe directly. For externally hosted designers
  moduleUrl?: string;       // load a designer JS module (auto-wires exports). For dynamically building the designer from an ESM module.
  iframeName?: string;      // iframe name attribute (default: 'designer-iframe')
  classes?: string[];       // CSS classes to add to the iframe. The designer uses tailwind, so these should be tailwind classes
  timeout?: number;         // ms to wait for ready signal (default: 10000)
};

Exactly one of url or moduleUrl is required.


DesignerInstance

The handle returned by createDesigner.

| Member | Description | |--------|-------------| | designer.init(payload) | Initialize the designer with config and product data | | designer.call(method, ...args) | Call any exported method on the designer (returns Promise) | | designer.on(event, handler) | Listen for events emitted by the designer (returns unsubscribe fn) | | designer.emit(event, payload) | Send an event into the designer | | designer.destroy() | Remove the iframe and clean up | | designer.iframe | The raw HTMLIFrameElement |


Designer Events

Events the designer emits that you can listen to with designer.on(...):

| Event | Payload | Description | |-------|---------|-------------| | save | { design, designId? } | User clicked save | | analytics | { event, payload } | Tracking event | | review | { product?, quantity } | User clicked "Add to Cart" / review | | persistence | { isDirty } | Design dirty state changed | | logoClick | {} | User clicked the logo |

Events you can send into the designer with designer.emit(...):

| Event | Payload | Description | |-------|---------|-------------| | saved | { success, designId?, error? } | Acknowledge a save response |


Examples

Minimal setup

<div id="designer-container" style="position:relative; width:100%; height:600px;"></div>

<script type="module">
  import DesignerManager from 'kronos';

  const designer = await DesignerManager.createDesigner({
    container: document.getElementById('designer-container'),
    moduleUrl: 'https://your-cdn.com/hermes/designer.js',
  });

  designer.init({
    product: { /* product config */ },
    endpoints: { /* API endpoints */ },
  });
</script>

Handling save

designer.on('save', async (payload) => {
  const resp = await fetch('/api/design', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ data: payload }),
  });

  const result = await resp.json();

  /* Acknowledge back to the designer (re-enables the save button, etc.) */
  designer.emit('saved', result);
});

Loading an existing design

const design = await fetch('/api/design/abc123').then(r => r.json());

designer.init({
  design,           // design to load
  product: { ... },
  endpoints: { ... },
});

Loading from a prebuilt URL

If the designer is deployed as a standalone page (e.g. a separate domain or CDN), use url instead of moduleUrl:

const designer = await DesignerManager.createDesigner({
  container: document.getElementById('designer-container'),
  url: 'https://designer.example.com/iframe.html',
});

Cleanup

/* Destroy the iframe and cleanup resources */
designer.destroy();

Extending types for a custom designer (TypeScript)

createDesigner accepts two type parameters — TAdditionalMethods and TEvents — so you can describe the full surface of your specific designer and get typed call, on, and emit throughout.

import DesignerManager, { DesignerInstance } from '@jamplus/kronos';

// Describe any methods your designer module exports beyond the base `init`
type MyCustomMethods = {
  getVersion: () => string;
  exportPng: (opts: { scale: number }) => Blob;
};

// Describe any events your designer emits beyond the built-ins
type MyCustomDesignerEvents = {
  fontsLoaded: { count: number };
};

const designer = await DesignerManager.createDesigner<MyMethods, MyEvents>({
  container: document.getElementById('designer-container')!,
  moduleUrl: 'https://your-cdn.com/hermes/designer.js',
});

/* call() and direct method access are now fully typed */
const version = await designer.call('getVersion');
const version2 = await designer.getVersion();

const png = await designer.call('exportPng', { scale: 2 });
const png2 = await designer.exportPng({scale: 2});

/* Your on() handlers will also receive the correct payload type */
designer.on('fontsLoaded', ({ count }) => {
  console.log(`${count} fonts loaded`);
});

// You can also alias the instance type for reuse elsewhere
type MyDesigner = DesignerInstance<MyMethods, MyEvents>;