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

@forgecart/designer-runtime

v1.202607161748.0

Published

ForgeCart visual-editor guest runtime for tenant storefronts: dormant iframe protocol endpoint, spray-region element resolution, and the Turbopack data-fc-source stamping loader

Readme

@forgecart/designer-runtime

The guest half of ForgeCart's visual editor. It ships inside tenant Next.js storefronts, boots dormant, and is activated exclusively over the iframe postMessage API by the dashboard host. Once active it resolves spray-stroke regions into DOM element descriptors (with file:line:col source attribution stamped at build time), renders highlight overlays, and reports scroll / resize / navigation so the host can keep its canvas in sync.

Three entrypoints:

| Subpath | Environment | Contents | |---------|-------------|----------| | . | Browser (ESM) | installDesignerRuntime(): () => void — the storefront mounts this once | | ./protocol | Anywhere (ESM, DOM-free) | Wire types, MessageCodec, ElementReferenceFormatter, protocol constants — the dashboard host and the agent import only this | | ./turbo-loader | Node (CJS) | The Turbopack/webpack loader that stamps data-fc-source onto host JSX elements in development |

The browser entrypoints have zero runtime dependencies; the loader subpath depends on @babel/parser + magic-string only.

Wire protocol (v1, namespace fc-designer)

Every message carries the envelope { ns: 'fc-designer', v: 1 }; anything else on the message bus (payment SDKs, analytics, HMR) is foreign traffic and ignored silently. All coordinates are iframe-viewport CSS pixels — the guest is zoom-agnostic by construction.

GUEST boots dormant ── READY (targetOrigin '*', empty payload) ──▶ HOST
HOST  ── HELLO { nonce, protocolVersion } ──▶ GUEST    nonce-gated, pins event.origin
GUEST ── HELLO_ACK { capabilities, url, viewport } ──▶ HOST       state: handshaken
HOST  ── ACTIVATE ──▶ GUEST ── ACTIVATED ──▶ HOST                 state: active
HOST  ── REGION_SELECT { requestId, stroke, radius, maxElements } ──▶ GUEST
GUEST ── REGION_RESOLVED { requestId, elements, regionBounds, truncated } ──▶ HOST
HOST  ── HIGHLIGHT_SET { refIds } / HIGHLIGHT_CLEAR ──▶ GUEST
HOST  ── ELEMENT_BOUNDS_QUERY { requestId, refIds } ──▶ GUEST ── ELEMENT_BOUNDS ──▶ HOST
HOST  ── PING ──▶ GUEST ── PONG { url, scroll } ──▶ HOST          liveness
GUEST ── SCROLL / RESIZE (rAF-throttled), NAVIGATED ──▶ HOST      while active
HOST  ── DEACTIVATE ──▶ GUEST ── DEACTIVATED ──▶ HOST             state: handshaken
GUEST ── ERROR { code, requestId, detail } ──▶ HOST               post-handshake only

Error codes: INVALID_MESSAGE, NOT_ACTIVE, REGION_TOO_LARGE, UNKNOWN_REF, INTERNAL — a wire-level string union; the host maps them to its own UX.

Activation security

  • Inert outside an iframewindow.parent === window makes installDesignerRuntime a no-op. The dormant footprint is exactly one message listener and zero DOM nodes.
  • Capability nonce — the host appends #fcd=<uuid> to the preview URL. The fragment never reaches the server; the guest parks the nonce in sessionStorage (survives iframe reloads) and strips only the fcd key from the hash. Without the nonce, every HELLO is ignored silently.
  • Origin pinning — a HELLO is accepted only from window.parent with a real (non-'null') origin and the exact nonce; the first success pins event.origin and every later post targets that origin. A re-HELLO must come from the pinned origin.
  • Reload recovery — an iframe reload boots a fresh runtime that posts a fresh READY beacon; the host re-handshakes and the nonce is still in sessionStorage.

Region resolution

REGION_SELECT rasterizes the stroke into grid cells (cell size max(8, radius/2), budget 600 cells with coarsening back-off; padded bbox beyond 4× the viewport area → REGION_TOO_LARGE), stack hit-tests every cell center, drops non-meaningful elements (document chrome, invisible, zero-area, > 85%-viewport wrappers, Next dev chrome), collapses ancestor/descendant stacks by coverage, ranks by coverage then area, caps at min(maxElements ?? 12, 25), and stamps survivors with data-fc-ref for later HIGHLIGHT_SET / ELEMENT_BOUNDS_QUERY. Stamps accumulate per activation and are stripped on DEACTIVATE.

Template integration (tenant storefront)

next.config.js — register the loader under turbopack.rules. With no as field Turbopack chains the loader into its built-in SWC, so the stamped TSX is still compiled normally. next build (webpack) ignores turbopack and the loader self-gates on NODE_ENV === 'development', so production output is untouched.

Glob gotcha (verified on Next 15.5): a rule key is matched by FILENAME unless it contains / (then by full project-relative path), and Turbopack does NOT expand braces. src/**/*.{tsx,jsx} therefore matches nothing and the loader silently never runs (data-fc-source count stays 0 with no error). Use per-extension filename globs:

module.exports = {
  turbopack: {
    rules: {
      '*.tsx': { loaders: ['@forgecart/designer-runtime/turbo-loader'] },
      '*.jsx': { loaders: ['@forgecart/designer-runtime/turbo-loader'] },
    },
  },
};

Mount the runtime last in the root layout's <body> via a 'use client' component so it survives App Router client navigations:

'use client';
import { useEffect } from 'react';

export function ForgecartDesigner(): null {
  useEffect(() => {
    if (process.env.NODE_ENV !== 'development') return; // DCE'd from prod bundles
    if (window.parent === window) return;
    let dispose: (() => void) | undefined;
    let cancelled = false;
    void import('@forgecart/designer-runtime').then((mod) => {
      if (cancelled) return;
      dispose = mod.installDesignerRuntime();
    });
    return () => {
      cancelled = true;
      dispose?.();
    };
  }, []);
  return null;
}

installDesignerRuntime is idempotent across React StrictMode's double effect: a second install while a runtime is live returns the live dispose, and disposing clears the singleton.

Host integration (dashboard)

Import only @forgecart/designer-runtime/protocol. The host:

  1. appends #fcd=<crypto.randomUUID()> to the preview URL before setting the iframe src;
  2. listens for READY, then posts HELLO { nonce, protocolVersion } to the iframe's content window — and re-handshakes whenever a fresh READY arrives (iframe reload);
  3. validates every inbound message with MessageCodec.parseGuestMessage and checks event.source is the iframe's content window;
  4. correlates request/response pairs by requestId with a timeout;
  5. serializes selections for the first message of an anchored AI thread via ElementReferenceFormatter (<fc-selection>{json}</fc-selection>).

Contingency: jsx-dev-runtime fallback

The loader runs under Turbopack (see the glob gotcha above — the config has to be right, but Turbopack does invoke it). If a future Next/Turbopack release breaks turbopack.rules for JS loaders entirely, the documented fallback is intercepting jsx-dev-runtime's jsxDEV source argument to inject the same attribute at render time. Note this fallback is dead on React 19.2, which drops the jsxDEV source argument — so the loader (which owns its own parse and is independent of React's dev runtime) is the only mechanism that survives the current stack, not merely the preferred one.

Develop, build, publish

nx run designer-runtime:test        # jsdom unit + loader integration suite
nx run designer-runtime:typecheck   # lib + loader + spec tsconfigs
nx run designer-runtime:build       # tsc ESM lib → dist/, tsc CJS loader → dist/loader-cjs/
nx run designer-runtime:publish     # npm publish --access public (runs build first)

The build stamps dist/loader-cjs/package.json with { "type": "commonjs" } so the loader subpath require()s correctly from this otherwise "type": "module" package.

Publish order for real workspace pods: publish this package first (the pod-image template warmup npm installs it through the Nexus npm proxy), then bump + commit the storefront template / CLI, then rebuild the workspace-pod image.