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

@usesophi/sophi-web-sdk

v0.1.0-beta.6

Published

Framework-agnostic SDK for embedding Sophi AI widget

Readme

Sophi Web SDK

npm version License: MIT

A framework-agnostic, TypeScript-first SDK for embedding the Sophi AI widget into any web application. Works seamlessly with vanilla JavaScript, React, Vue, Svelte, Angular, Next.js, and more.

Features

  • 🚀 Framework Agnostic - Works with any JavaScript framework or vanilla JS
  • 📦 Zero Dependencies - Lightweight with no runtime dependencies
  • 🔒 Secure - Built-in authentication and origin validation
  • 🔑 Automatic Session Management - Handles authentication automatically
  • 💪 TypeScript First - Full type definitions included
  • 🎯 Event-Driven - Clean event-based API for loose coupling
  • 📱 Modern - ESM and CommonJS support with tree-shaking
  • 🎨 Flexible - Client controls all UX (toasts, errors, styling)
  • 📁 Well-Structured - Modular architecture for easy maintenance

Security Note

Q: Will users see my API URLs?
A: Yes, and that's okay!

API URLs in client-side code are always visible to users (they can see them in browser DevTools network tab). This is normal and safe because:

  • ✅ API URLs themselves aren't secrets - they're just endpoints
  • Real security comes from your apiKey and clientId validation on the server
  • ✅ Your API should authenticate and authorize every request
  • ✅ Never put actual secrets (API keys, tokens) in client code - they come from your backend

Think of API URLs like a street address - it's public information. The security is the lock on the door (your API authentication).

Installation

npm install sophi-web-sdk

Or with yarn:

yarn add sophi-web-sdk

Or with pnpm:

pnpm add sophi-web-sdk

Quick Start

Vanilla JavaScript

import { SophiWidget } from "sophi-web-sdk";

const widget = new SophiWidget();

// Initialize with authentication (async)
await widget.init({
  apiKey: "your-api-key", // From parent app
  clientId: "your-client-id", // From parent app
  userId: "user-12345", // Current user ID
  container: "#sophi-widget",
  environment: "production", // API URL determined automatically
  metadata: {
    // Optional metadata
    theme: "dark",
    plan: "premium",
  },
  onReady: () => console.log("Widget ready!"),
});

// Listen for events
widget.on("add_to_cart", (data) => {
  console.log("Products to add:", data.products);
  // Handle cart logic here
});

Note: The init() method is now async and automatically handles authentication with the Sophi API before loading the widget. The API URL is determined automatically based on the environment setting.

React

import { useEffect, useRef } from "react";
import { SophiWidget } from "sophi-web-sdk";

function MyComponent() {
  const widgetRef = useRef<SophiWidget | null>(null);

  useEffect(() => {
    const widget = new SophiWidget();
    widgetRef.current = widget;

    // Async initialization
    const initWidget = async () => {
      try {
        await widget.init({
          apiKey: "your-api-key",
          clientId: "your-client-id",
          userId: "user-12345",
          container: "#sophi-widget",
          environment: "production",
          metadata: { theme: "dark" },
        });

        widget.on("add_to_cart", (data) => {
          // Handle add to cart
        });
      } catch (error) {
        console.error("Failed to initialize widget:", error);
      }
    };

    initWidget();

    return () => widget.destroy();
  }, []);

  return <div id="sophi-widget" />;
}

See examples/react-example/ for a complete React example with custom hooks.

Vue 3

<template>
  <div id="sophi-widget"></div>
</template>

<script setup>
import { onMounted, onUnmounted } from "vue";
import { SophiWidget } from "sophi-web-sdk";

let widget = null;

onMounted(() => {
  widget = new SophiWidget();

  widget.init({
    apiKey: "your-api-key",
    container: "#sophi-widget",
  });

  widget.on("add_to_cart", (data) => {
    // Handle add to cart
  });
});

onUnmounted(() => {
  if (widget) {
    widget.destroy();
  }
});
</script>

Svelte

<script>
  import { onMount, onDestroy } from 'svelte';
  import { SophiWidget } from 'sophi-web-sdk';

  let widget;

  onMount(() => {
    widget = new SophiWidget();

    widget.init({
      apiKey: 'your-api-key',
      container: '#sophi-widget',
    });

    widget.on('add_to_cart', (data) => {
      // Handle add to cart
    });
  });

  onDestroy(() => {
    if (widget) {
      widget.destroy();
    }
  });
</script>

<div id="sophi-widget"></div>

Angular

import { Component, OnInit, OnDestroy } from "@angular/core";
import { SophiWidget } from "sophi-web-sdk";

@Component({
  selector: "app-widget",
  template: '<div id="sophi-widget"></div>',
})
export class WidgetComponent implements OnInit, OnDestroy {
  private widget: SophiWidget | null = null;

  ngOnInit() {
    this.widget = new SophiWidget();

    this.widget.init({
      apiKey: "your-api-key",
      container: "#sophi-widget",
    });

    this.widget.on("add_to_cart", (data) => {
      // Handle add to cart
    });
  }

  ngOnDestroy() {
    if (this.widget) {
      this.widget.destroy();
    }
  }
}

API Reference

SophiWidget

The main class for interacting with the Sophi widget.

init(config: SophiConfig): Promise<void>

Initialize the widget with configuration. This method is now async and handles authentication automatically.

await widget.init({
  // Required fields
  apiKey: "your-api-key", // Your API key (x-api-key header)
  clientId: "your-client-id", // Client ID (x-sophi-client-id header)
  userId: "user-12345", // External user ID from your app
  container: "#sophi-widget", // CSS selector or HTMLElement

  // Optional fields
  environment: "production", // 'test' | 'staging' | 'production' (determines API URL)
  metadata: {
    // Custom metadata for the session
    theme: "dark",
    plan: "premium",
  },
  width: "100%", // Widget width (default: '100%')
  height: "600px", // Widget height (default: '600px')
  onReady: () => {}, // Callback when widget is ready
  onError: (error) => {}, // Callback for errors
});

Authentication Flow:

  1. SDK validates configuration
  2. SDK determines API URL based on environment setting
  3. SDK calls authentication API with apiKey, clientId, and userId
  4. API returns session data (accessToken, sessionId, etc.)
  5. SDK creates iframe and sends session data via postMessage
  6. Widget is ready to use

Environment to API URL Mapping:

  • test: Test API URL (for development/testing)
  • staging: Staging API URL
  • production: Production API URL (default)

See AUTHENTICATION.md for detailed authentication documentation.

sendUserData(data: UserData): void

Send user data to the widget via postMessage.

widget.sendUserData({
  userId: "user-123",
  email: "[email protected]",
  name: "John Doe",
  // Add any custom fields
  customField: "value",
});

on<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void

Register an event listener.

// Listen for add_to_cart events
widget.on("add_to_cart", (data) => {
  console.log("Products:", data.products);
});

// Listen for ready events
widget.on("ready", () => {
  console.log("Widget is ready");
});

// Listen for error events
widget.on("error", (error) => {
  console.error("Widget error:", error);
});

off<K extends EventName>(event: K, handler: EventHandler<SophiEventMap[K]>): void

Unregister an event listener.

const handler = (data) => console.log(data);
widget.on("add_to_cart", handler);
widget.off("add_to_cart", handler);

show(): void

Show the widget (sets display to 'block').

widget.show();

hide(): void

Hide the widget (sets display to 'none').

widget.hide();

destroy(): void

Destroy the widget and cleanup all resources.

widget.destroy();

isReady(): boolean

Check if the widget is initialized and ready.

if (widget.isReady()) {
  widget.sendUserData({ userId: "123" });
}

TypeScript Support

The SDK is written in TypeScript and includes full type definitions.

import { SophiWidget, SophiConfig, AddToCartEvent, UserData } from "sophi-web-sdk";

const config: SophiConfig = {
  apiKey: "your-api-key",
  container: "#widget",
};

const widget = new SophiWidget();
widget.init(config);

widget.on("add_to_cart", (data: AddToCartEvent) => {
  data.products.forEach((product) => {
    console.log(product.productId, product.variantId);
  });
});

Events

add_to_cart

Fired when the widget requests to add products to the cart.

interface AddToCartEvent {
  products: Array<{
    productId: string;
    variantId?: string;
  }>;
}

widget.on("add_to_cart", (data: AddToCartEvent) => {
  // Handle cart logic
  // Show toast notifications
  // Update cart UI
});

ready

Fired when the widget iframe is ready.

widget.on("ready", () => {
  console.log("Widget is ready");
});

error

Fired when an error occurs.

widget.on("error", (error: Error) => {
  console.error("Error:", error.message);
});

Security

The SDK includes comprehensive security features:

  • Authentication: Automatic session creation with JWT tokens
  • Origin Validation: All postMessage events are validated against the iframe origin
  • Message Validation: Incoming messages are validated for correct structure
  • Type Safety: TypeScript ensures type-safe message handling
  • Secure Headers: API key and client ID sent via secure headers
  • Token Expiration: Session tokens include expiration timestamps

See AUTHENTICATION.md for security best practices.

Documentation

Examples

Check out the examples/ directory for complete working examples:

  • examples/vanilla-html/ - Pure HTML/JS example with no build step
  • examples/react-example/ - React example with custom hooks

To run the examples:

# Vanilla HTML example
cd examples/vanilla-html
# Open index.html in your browser

# React example
cd examples/react-example
npm install
npm run dev

Both examples include the new authentication flow with all required fields.

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT © Sophi

Support

For issues and questions, please open an issue on GitHub or contact [email protected].