@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-widgetBuild Artifacts
After npm run build the following distributables are generated:
dist/embed/widget.iife.js– browser-ready bundle exposing the globalAIFindrCommerceWidgetdist/embed/widget.esm.js/widget.cjs– module builds for bundlers or CommonJS environmentsdist/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 insideoptions.renderTorenderTo(string | HTMLElement) – CSS selector or element that will host the iframeclientId(string) – AIFindr Commerce client idbaseUrl(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 completeswidget.isReady()– boolean flag for the handshake statewidget.on(event, listener)/widget.off(event, listener)– subscribe/unsubscribe to widget lifecycle & commerce eventswidget.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 scriptclient.getContext()– get the current context payloadclient.emitEvent(event, data)– send events to the host pageclient.emitError(error)– report recoverable or fatal errors back to the host pageclient.ready(callback?)– returns a promise or accepts a callback triggered after handshake completesclient.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/iframeFor 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-containery 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 eventoscommerce.product.selectedycommerce.product.cta. Todas las acciones muestranalert()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:orbreaking:trigger a major version bump (1.0.0 → 2.0.0) - Commits starting with
feat:,feature:, orminor: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:
- Version bump in package.json based on commit messages
- Git tag creation (e.g.,
v1.2.3) - GitHub Release with auto-generated changelog
- npm package publication
- 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-nameOpen 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.1Example 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.0Example 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.0Example 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 releaseManual Release Trigger
To manually trigger a release without merging to main:
- Go to the Actions tab in GitHub
- Select the "Publish to npm" workflow
- Click "Run workflow"
- Choose the branch (usually
main) - Select the release type:
patch,minor, ormajor - 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:
- 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.
