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

@aifindr/agent-widget

v0.0.3

Published

Embeddable AIFindr Agent Widget and iframe bridge.

Readme

AIFindr Agent Widget

Embeddable JavaScript widget that renders the AIFindr conversational agent experience inside any website and a companion iframe client to integrate the widget UI. The package supports both static script distribution for simple website integration and npm package distribution for modern build tools.

Distribution Methods

This package provides two integration approaches:

1. Static Distribution (CDN/Script Tag)

Use prebuilt JavaScript files for direct integration into any website without a build system.

2. npm Package Distribution

Install via npm/pnpm for projects using modern bundlers (Vite, Webpack, etc.).

npm install @aifindr/agent-widget
# or
pnpm add @aifindr/agent-widget

Build Artifacts

After npm run build the following distributables are generated:

  • dist/embed/widget.iife.js – browser-ready bundle exposing the global AIFindrCommerceWidget
  • dist/embed/widget.esm.js / widget.cjs – module builds for bundlers or CommonJS environments
  • dist/iframe/client.esm.js / client.cjs – iframe-side bridge for the hosted widget UI
  • Type definitions are emitted next to each build (.d.ts)

Host Page Integration (Embedding the Widget)

The host page is where you want the widget to appear. Choose the integration method that fits your project:

Option A: Static Script Integration (No Build Tools)

Include the CDN bundle on the page where the widget must appear, then instantiate the widget once the DOM is ready:

<!-- Load from your CDN bucket -->
<script src="/path/to/widget.iife.js" data-client-id="TU_CLIENT_ID" defer></script>
<script>
  window.addEventListener('DOMContentLoaded', () => {
    const widget = new AIFindrCommerceWidget({
      renderTo: '#aifindr-widget-container',
      clientId: 'TU_CLIENT_ID',
      baseUrl: 'https://client.aifindrcommerce.ai/widget.html'
    });

    widget
      .ready(() => console.log('Widget ready!'))
      .on(AIFindrCommerceWidget.EVENTS.PRODUCT_SELECTED, (data) => {
        console.log('Product selected:', data.productId);
      });
  });
</script>

Option B: npm Package Integration (With Build Tools)

Install the package and import it in your JavaScript/TypeScript project:

import AIFindrCommerceWidget from '@aifindr/agent-widget';

// Initialize the widget
const widget = new AIFindrCommerceWidget({
  renderTo: '#aifindr-widget-container',
  clientId: 'YOUR_CLIENT_ID',
  baseUrl: 'https://client.aifindrcommerce.ai/widget.html',
  context: { /* optional initial context */ }
});

// Listen to events
widget.ready(() => {
  console.log('Widget ready!');
});

widget.on(AIFindrCommerceWidget.EVENTS.PRODUCT_SELECTED, (data) => {
  console.log('Product selected:', data.productId);
});

widget.on(AIFindrCommerceWidget.EVENTS.MESSAGE_SENT, (data) => {
  console.log('Message sent:', data.message);
});

// Cleanup when done
// widget.destroy();

Widget API Overview

  • new AIFindrCommerceWidget(options) – renders the iframe inside options.renderTo
    • renderTo (string | HTMLElement) – CSS selector or element that will host the iframe
    • clientId (string) – AIFindr Commerce client id
    • baseUrl (string) – URL of the iframe application; the widget appends ?client_id=…&handshake=…
    • context (object, optional) – initial contextual data propagated to the iframe
  • widget.ready(callback?) – returns a promise or accepts a callback triggered after the iframe handshake completes
  • widget.isReady() – boolean flag for the handshake state
  • widget.on(event, listener) / widget.off(event, listener) – subscribe/unsubscribe to widget lifecycle & commerce events
  • widget.destroy() – dispose the widget instance and remove the iframe

All available event names are exposed through AIFindrCommerceWidget.EVENTS:

| Event | Description | |-------|-------------| | widget.ready | Widget initialised and handshake completed | | widget.error | An error was reported by the iframe | | conversation.started | New conversation initiated | | message.sent | User sent a message | | message.received | AI reply received | | commerce.product.selected | Product selected inside the conversation | | commerce.product.cta | Product CTA triggered |


Iframe Application Integration (Widget UI)

The iframe application is the actual widget UI that runs inside the iframe. This is where you build your conversational agent interface. Choose the integration method that fits your project:

Option A: Static Script Integration (No Build Tools)

Load the client bundle from your CDN and use it as an ES module:

<script type="module">
  import AIFindrCommerceWidgetClient from '/assets/iframe/client.esm.js';

  const client = new AIFindrCommerceWidgetClient();

  client.ready(() => {
    console.log('Client ready! Initial context:', client.getContext());
    console.log('Client ID:', client.getClientId());
  });

  // Emit events to the host page
  function sendMessage(content) {
    client.emitEvent(AIFindrCommerceWidgetClient.EVENTS.MESSAGE_SENT, {
      message: content,
      timestamp: Date.now()
    });
  }

  function selectProduct(productId) {
    client.emitEvent(AIFindrCommerceWidgetClient.EVENTS.PRODUCT_SELECTED, {
      productId,
      timestamp: Date.now()
    });
  }
</script>

Option B: npm Package Integration (With Build Tools)

Install the package and import the iframe client in your project:

import AIFindrCommerceWidgetClient from '@aifindr/agent-widget/iframe';

const client = new AIFindrCommerceWidgetClient();

client.ready(() => {
  console.log('Client ready! Initial context:', client.getContext());
  console.log('Client ID:', client.getClientId());
});

// Emit events to the host page
export function sendMessage(content: string) {
  client.emitEvent(AIFindrCommerceWidgetClient.EVENTS.MESSAGE_SENT, {
    message: content,
    timestamp: Date.now()
  });
}

export function selectProduct(productId: string) {
  client.emitEvent(AIFindrCommerceWidgetClient.EVENTS.PRODUCT_SELECTED, {
    productId,
    timestamp: Date.now()
  });
}

// Handle errors
export function reportError(error: Error) {
  client.emitError(error);
}

Client API Overview

The client library handles the handshake protocol, validates the origin, and relays events back to the host. It exposes these methods:

  • client.getClientId() – fetch the client id provided by the embed script
  • client.getContext() – get the current context payload
  • client.emitEvent(event, data) – send events to the host page
  • client.emitError(error) – report recoverable or fatal errors back to the host page
  • client.ready(callback?) – returns a promise or accepts a callback triggered after handshake completes
  • client.destroy() – detach listeners if the iframe app tears down

Usage Examples

Example: Using in a React Application

If you're building your iframe application with React, here's how to use the plain JavaScript client library:

import { useEffect, useRef, useState } from 'react';
import AIFindrCommerceWidgetClient from '@aifindr/agent-widget/iframe';

function App() {
  const clientRef = useRef<AIFindrCommerceWidgetClient | null>(null);
  const [context, setContext] = useState<any>(null);
  const [isReady, setIsReady] = useState(false);

  useEffect(() => {
    // Initialize the client
    const client = new AIFindrCommerceWidgetClient();
    clientRef.current = client;

    client.ready(() => {
      console.log('Client ready');
      setIsReady(true);
      setContext(client.getContext());
    });

    // Cleanup on unmount
    return () => {
      client.destroy();
    };
  }, []);

  const handleProductSelected = (productId: string) => {
    if (clientRef.current) {
      clientRef.current.emitEvent(
        AIFindrCommerceWidgetClient.EVENTS.PRODUCT_SELECTED,
        { productId, timestamp: Date.now() }
      );
    }
  };

  const handleSendMessage = (message: string) => {
    if (clientRef.current) {
      clientRef.current.emitEvent(
        AIFindrCommerceWidgetClient.EVENTS.MESSAGE_SENT,
        { message, timestamp: Date.now() }
      );
    }
  };

  if (!isReady) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>AIFindr Widget (clientId: {clientRef.current?.getClientId()})</h1>
      {/* Your React components here */}
    </div>
  );
}

export default App;

Example: Custom React Hook

For better reusability, you can create a custom hook:

import { useEffect, useRef, useState } from 'react';
import AIFindrCommerceWidgetClient from '@aifindr/agent-widget/iframe';

export function useAIFindrClient() {
  const clientRef = useRef<AIFindrCommerceWidgetClient | null>(null);
  const [isReady, setIsReady] = useState(false);
  const [context, setContext] = useState<any>(null);

  useEffect(() => {
    const client = new AIFindrCommerceWidgetClient();
    clientRef.current = client;

    client.ready(() => {
      setIsReady(true);
      setContext(client.getContext());
    });

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

  const emitEvent = (event: string, data: any) => {
    if (clientRef.current && isReady) {
      clientRef.current.emitEvent(event, data);
    }
  };

  const emitError = (error: Error) => {
    if (clientRef.current) {
      clientRef.current.emitError(error);
    }
  };

  return {
    client: clientRef.current,
    isReady,
    context,
    clientId: clientRef.current?.getClientId(),
    emitEvent,
    emitError,
  };
}

// Usage in a component:
function ProductCard({ product }) {
  const { emitEvent, isReady } = useAIFindrClient();

  const handleClick = () => {
    if (isReady) {
      emitEvent(AIFindrCommerceWidgetClient.EVENTS.PRODUCT_SELECTED, {
        productId: product.id,
        timestamp: Date.now(),
      });
    }
  };

  return <button onClick={handleClick}>Select Product</button>;
}

Development Workflow

npm install
npm run build      # generates dist/ bundles and type definitions
# During local iteration with demo pages
npm run dev        # abre http://localhost:5173 con el flujo host/iframe

For local iteration, point a static server at /dist/embed/widget.iife.js for the host page and /dist/iframe/client.esm.js for the iframe UI. All assets are pure ES2019 JavaScript and require no runtime dependencies.

Interactive Demo

Ejecuta npm run dev para arrancar un servidor Vite que expone dos páginas:

  • /index.html – página host que renderiza el widget dentro del contenedor #aifindr-widget-container y muestra los eventos recibidos en tiempo real.
  • /content.html – página que simula la interfaz del widget dentro del iframe. Contiene tarjetas de productos con CTAs que disparan los eventos commerce.product.selected y commerce.product.cta. Todas las acciones muestran alert() para visualizar el flujo.

El host escucha los eventos publicados mediante postMessage. El iframe, implementado con AIFindrCommerceWidgetClient, emite esos eventos cuando el usuario interactúa con la UI de muestra.

CI/CD & Publishing

Automated Workflows

This repository includes GitHub Actions workflows for continuous integration and automated publishing:

PR Check Workflow

Every pull request automatically runs:

  • Clean build verification (npm run clean && npm run build)
  • Build artifact validation (ensures all expected files are generated)
  • Package validity check (npm pack --dry-run)

This ensures that no PR can be merged if it would cause publishing issues.

Automated npm Publishing

When code is merged to main, the package is automatically published to npm with:

Semantic Versioning (Conventional Commits):

  • Commits starting with major: or breaking: trigger a major version bump (1.0.0 → 2.0.0)
  • Commits starting with feat:, feature:, or minor: trigger a minor version bump (1.0.0 → 1.1.0)
  • All other commits trigger a patch version bump (1.0.0 → 1.0.1)

Manual Publishing: You can also trigger a release manually from the Actions tab with your choice of version bump type.

What happens automatically:

  1. Version bump in package.json based on commit messages
  2. Git tag creation (e.g., v1.2.3)
  3. GitHub Release with auto-generated changelog
  4. npm package publication
  5. Summary with links to npm and GitHub release

How to Trigger a Release

To publish a new version to npm and GitHub, follow these steps:

1. Create your feature branch and make changes

git checkout -b feature/your-feature-name
# Make your changes...
git add .

2. Commit with the appropriate prefix

Use conventional commit prefixes to control the version bump:

For Patch Release (1.0.0 → 1.0.1) - Bug fixes, documentation, chores:

git commit -m "fix: resolve iframe handshake timeout issue"
git commit -m "docs: update integration examples"
git commit -m "chore: update dependencies"
git commit -m "style: improve code formatting"

For Minor Release (1.0.0 → 1.1.0) - New features (backwards-compatible):

git commit -m "feat: add support for custom event handlers"
git commit -m "feature: implement lazy loading for iframe"
git commit -m "minor: add new widget configuration option"

For Major Release (1.0.0 → 2.0.0) - Breaking changes:

git commit -m "breaking: change widget initialization API signature"
git commit -m "major: remove deprecated methods from client API"
git commit -m "breaking: require clientId parameter in constructor"

3. Create a Pull Request

git push origin feature/your-feature-name

Open a PR on GitHub. The PR checks will automatically verify that your changes build correctly.

4. Merge to main

Once approved and merged to main, the publish workflow will automatically:

  • Detect the version bump type from your commit message
  • Bump the version in package.json
  • Build the project
  • Create a git tag (e.g., v1.2.3)
  • Generate a changelog from commits
  • Create a GitHub Release
  • Publish to npm

5. Verify the Release

After the workflow completes, verify:

  • npm package: https://www.npmjs.com/package/@aifindr/agent-widget
  • GitHub Release: Check the Releases page for the new version

Examples

Example 1: Bug fix for patch release

git checkout -b fix/event-listener-memory-leak
# Fix the memory leak...
git add .
git commit -m "fix: prevent memory leak in event listener cleanup"
git push origin fix/event-listener-memory-leak
# Create PR, merge to main → triggers 1.0.0 → 1.0.1

Example 2: New feature for minor release

git checkout -b feature/custom-styles
# Add custom styling support...
git add .
git commit -m "feat: add customStyles option to widget configuration"
git push origin feature/custom-styles
# Create PR, merge to main → triggers 1.0.1 → 1.1.0

Example 3: Breaking change for major release

git checkout -b breaking/new-init-api
# Refactor initialization API...
git add .
git commit -m "breaking: replace options object with required parameters

BREAKING CHANGE: Widget constructor now requires clientId and baseUrl
as separate parameters instead of an options object."
git push origin breaking/new-init-api
# Create PR, merge to main → triggers 1.1.0 → 2.0.0

Example 4: Multiple commits in one PR

If your PR has multiple commits, the workflow will use the first non-release commit to determine the version bump:

git checkout -b feature/accessibility-improvements
git commit -m "feat: add ARIA labels to widget controls"
git commit -m "fix: improve keyboard navigation"
git commit -m "docs: update accessibility section in README"
# The "feat:" commit determines the bump → minor release

Manual Release Trigger

To manually trigger a release without merging to main:

  1. Go to the Actions tab in GitHub
  2. Select the "Publish to npm" workflow
  3. Click "Run workflow"
  4. Choose the branch (usually main)
  5. Select the release type: patch, minor, or major
  6. Click "Run workflow"

This is useful for hotfixes or when you need to override the automatic version detection.

Setup Requirements

To enable automated publishing, configure the npm token secret in your GitHub repository:

  1. NPM_TOKEN: Create a personal automation token
    • Go to https://www.npmjs.com/settings/YOUR_USERNAME/tokens
    • Click "Generate New Token" → Select "Automation" type
    • Copy the token
    • Add it to GitHub: Repository Settings → Secrets and variables → Actions → New repository secret
    • Name: NPM_TOKEN, Value: (paste token)

Important: The token must be created by a user who is a member of the theagilemonkeys organization with publish permissions. The token will inherit your organization member permissions, allowing it to publish packages to the @aifindr scope.

The workflow uses GitHub's built-in GITHUB_TOKEN for creating releases and tags.

License

Apache-2.0

Copyright 2025 The Agile Monkeys Inc.