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

@synapcores/widget

v0.4.0

Published

Drop-in chat widget powered by SynapCores. One <script> tag, MIT, browser-side credentials never required.

Downloads

157

Readme

@synapcores/widget

Drop-in chat widget powered by SynapCores. Embed it on any site with one <script> tag; the SynapCores engine is the brain (recall memory + RAG + tool routing + grounded generation via AGENT_RUN() in-DB).

Status — Sprint 2 Phase B (v0.2.1-proxy). The widget no longer talks to the SynapCores gateway directly. It talks to a tiny Node.js proxy (sibling crate at ../proxy/) that holds the SynapCores credential server-side. The browser holds only an HttpOnly signed session cookie. See ../proxy/README.md for the proxy setup; see this README for the widget surface.


Architecture

Browser                  widget-proxy (Node.js)      SynapCores gateway
─────────                ──────────────────────       ──────────────────
<script /widget.js>
  │ POST /v1/session     ─► validates origin,
  │  {project_key}          sets HttpOnly cookie
  │ ◄─ {persona, agent_name, …}
  │
  │ WS /ws (cookie auth) ─► verifies cookie,
  │                         opens upstream WS,
  │                         pipes AiChatWsMessage   ─► AGENT_RUN in-DB
  │ ◄─ message_chunk          ◄─                       ◄─
  │ ◄─ message_complete       ◄─                       ◄─

Browser holds: an HttpOnly signed cookie. Nothing else. Proxy holds: the SynapCores JWT/API key, project allowlist, rate limits. SynapCores: runs AGENT_RUN() natively.

One credential, one proxy, one engine. No Python, no DB token in JS.


Install (production)

The embedder hosts the proxy. The site author pastes:

<script
  defer
  src="https://chat.your.com/widget.js"
  data-api-base="https://chat.your.com"
  data-project-key="pk_abc123"
></script>

Where https://chat.your.com is the widget-proxy URL. The proxy's projects.json defines pk_abc123 with its tenant, database, persona, upstream SynapCores credential, allowed origins, and rate limit.

For cross-origin embeds (proxy on a different origin from the host site), set session.same_site_none = true in the proxy config — required for the cookie to travel cross-origin. HTTPS is then mandatory.

Install (development)

# Run the proxy (this also serves the widget bundle and a dev landing page)
cd ../proxy
npm install
export PROXY_SESSION_SECRET="$(openssl rand -hex 32)"
export DEMO_SYNAPCORES_TOKEN="<a JWT from your SynapCores /v1/auth/login>"
cp projects.example.json projects.json
npm start                                   # http://127.0.0.1:5060

# Iterate on widget code (rebuilds dist/widget.js on every save)
cd ../widget
npm run dev

Open http://127.0.0.1:5060/. The proxy renders a dev landing page script-tagging the widget against the first configured project.


Config

| Attribute | Default | Notes | | ---------------------- | -------------------- | -------------------------------------------------------- | | data-api-base | (required) | widget-proxy URL | | data-project-key | (required) | proxy looks up tenant/database/persona/allowed_origins | | data-agent-name | proxy default | Header label (proxy supplies per-project default) | | data-greeting | sensible | First message shown when the panel opens | | data-primary-color | #00bfff | Any CSS color | | data-position | bottom-right | bottom-right / bottom-left / top-right / top-left| | data-theme | auto | light / dark / auto | | data-show-branding | true | Set false to hide "Powered by SynapCores" | | data-model | server default | Optional override sent in send_message.model |

Or via JS API:

<script defer src=".../widget.js"></script>
<script>
  window.addEventListener('DOMContentLoaded', () => {
    const w = window.SynapCores.init({
      apiBase: 'https://chat.your.com',
      projectKey: 'pk_abc123',
      primaryColor: '#7c3aed',
    });
    // w.open(), w.close(), w.toggle(), w.send(text), w.destroy()

    // When you know who the visitor is — e.g. after a login event —
    // call identify(). The proxy stores it and AGENT_RUN sees the
    // visitor as identified on every subsequent turn:
    w.identify({
      id: 'u_42',
      name: 'Luis',
      email: '[email protected]',
      attrs: { plan: 'pro' },
    });
    // Or call from the global — applies to every mounted widget:
    window.SynapCores.identify({ id: 'u_42', name: 'Luis' });
  });
</script>

What this widget does on the wire

| Browser → proxy → upstream | Direction | | --------------------------------------------------------- | --------- | | POST /v1/session {project_key, visitor_id?} | HTTPS | | WS /ws upgrade (cookie auth) | Browser → proxy | | {type:"send_message", session_id, message, model?} | Browser → proxy | | {type:"message_chunk", message_id, session_id, chunk} | Proxy → browser | | {type:"message_complete", message_id, full_message} | Proxy → browser | | {type:"error", message, code} | Proxy → browser |

The proxy injects context.database and context.visitor_id upstream — the widget cannot point at another tenant's database, and tries to execute_sql from the browser are silently dropped at the proxy.


What's in Sprint 3 (v0.3.0-identity)

  • identify(attrs) on the Widget instance + window.SynapCores.identify(attrs) global. Stored on the proxy and threaded into send_message.context.user server-side — AGENT_RUN sees an identified visitor on every turn. Safe to call before the panel opens; replayed once the session cookie is set. The proxy holds it in-memory keyed by visitor id (TTL 24h); persisting to SynapCores is a future hardening pass.
  • Persistent conversation across page loads + days. Server-supplied deterministic session_id (HMAC of secret + visitor + project) replaces the per-mount UUID. On open, the widget fetches GET /v1/history and re-renders prior turns before showing the composer — visitors returning later see the conversation they had.
  • widget.identity read-only getter — what the embedder last identified (useful for debug overlays).
  • All Sprint 1/2 polish unchanged.

What's pending

  • Sprint 4 (#321): CDN publish (Cloudflare Pages — free), npm @synapcores/widget publish, Dockerfile for the proxy, docker-compose with synapcores + proxy + first project, minimal admin HTML page, example embeds (bare HTML / Next.js / WordPress).

Source layout

widget/src/
  index.ts        public API + auto-init from <script data-*>
  widget.ts      UI + session bootstrap + AiChatWsMessage protocol
  config.ts      types, defaults, data-* parsing, deriveWsUrl()
  session.ts     POST /v1/session (cookie-set, no token in response body)
  visitor.ts     crypto.randomUUID() → localStorage → cookie → in-memory
  ws.ts          WebSocket with exponential-backoff reconnect
  theme.ts       primary color + position + dark/light auto
  dom.ts         el() factory + focus-trap helper
  markdown.ts    safe markdown renderer
  styles.css     scoped, inlined into the bundle at build time

Build

npm install
npm run build       # → dist/widget.js (21.3 KB minified, CSS inlined)
npm run dev         # esbuild watch only (proxy serves the bundle)

License

MIT — see LICENSE.