@stepbeam/sdk
v0.1.1
Published
Customer-facing SDK for rendering product guides and NPS surveys
Maintainers
Readme
StepBeam SDK
The customer-facing JavaScript SDK for rendering interactive product guides and NPS surveys.
Links: Homepage · Admin & sign-up · Issues
Features
- Multiple Step Types: Support for modal, tooltip, and hotspot guides
- Smart Triggers: Automatically show guides based on page load or element visibility
- Multi-Language Support: Built-in i18n with auto-detection, RTL support, and 12+ languages
- NPS Surveys: Display and collect Net Promoter Score surveys
- Customizable Styling: Pre-built styles with full customization options
- Event Callbacks: Track user progress through guides
- Lightweight: No external dependencies
- TypeScript Support: Full type definitions included
Installation
Option 1: NPM Package
npm install @stepbeam/sdkOption 2: Script Tag (recommended for quick setup)
Drop a single async-loader snippet into your site's <head>. The SDK loads
non-blocking and queues any calls made before it's ready, so you can call
StepBeam('init', ...) immediately after the loader without waiting for a
callback:
<!-- StepBeam SDK -->
<script>
(function(p,r,o,d,u,c,t){
p[u]=p[u]||function(){(p[u]._q=p[u]._q||[]).push(arguments)};
p[u]._q=p[u]._q||[];p[u]._appKey=d;p[u]._cdnUrl=o;
c=r.createElement('script');c.async=1;c.src=o+'/stepbeam.min.js';
c.setAttribute('data-app-key',d);
t=r.getElementsByTagName('script')[0];t.parentNode.insertBefore(c,t);
})(window,document,'https://sdk.stepbeam.com','YOUR_APP_KEY','StepBeam');
StepBeam('init', {
appKey: 'YOUR_APP_KEY',
apiUrl: 'https://api.stepbeam.com',
});
</script>Replace YOUR_APP_KEY with the app key from your StepBeam admin
(https://admin.stepbeam.com/admin/settings/applications). The admin's
Onboarding Setup page also generates a per-application snippet you can
copy directly.
The SDK bundle:
- Loads asynchronously — no impact on page load
- Is served from
https://sdk.stepbeam.com/stepbeam.min.js - Self-installs as
window.StepBeam - Contains all SDK functionality in a single ~181 KB minified bundle
Quick Start
SDK Initialization
Before using any SDK functionality, you must initialize the SDK with your API key and API URL:
import { init, getSiteId, isInitialized, getConfig } from '@stepbeam/sdk';
// Initialize the SDK with your API key and API URL (both required)
await init('pk_live_your_api_key_here', {
apiUrl: 'https://api.stepbeam.com' // Required!
});
// For local development:
await init('pk_live_your_api_key_here', {
apiUrl: 'http://localhost:3011'
});
// Check initialization status
console.log(isInitialized()); // true
// Retrieve the site ID (returned from authentication)
console.log(getSiteId()); // 'site_abc123'
// Get full configuration
const config = getConfig();
console.log(config.accountName); // Your account name
console.log(config.apiUrl); // API endpointImportant Notes:
init()is async and authenticates with the StepBeam backendinit()must be called before using any other SDK functionsinit()can only be called once per session- Calling
init()twice throwsSDKAlreadyInitializedError - Calling SDK functions before
init()throwsSDKNotInitializedError - Invalid API keys throw
SDKAuthenticationError
Using StepBeam
import { init, StepBeam, getApiUrl } from '@stepbeam/sdk';
// First, initialize the SDK with your API key
await init('pk_live_your_api_key_here');
// Then create a StepBeam instance
const guide = new StepBeam({
apiUrl: getApiUrl(), // Use the authenticated API URL
applicationId: 'your-app-id',
appKey: 'your-app-key',
userId: 'user-123', // Required: identifies the guide viewer
userProperties: { // Optional: enrich events with user context
plan: 'pro',
country: 'US',
role: 'admin',
},
onStepComplete: (step) => {
console.log('Step completed:', step.title);
},
onGuideComplete: (guide) => {
console.log('Guide completed:', guide.title);
},
onGuideClose: () => {
console.log('Guide closed');
}
});
// Load and start a guide
async function startGuide() {
await guide.loadGuide('guide-id');
guide.start();
}
startGuide();Package Structure
The SDK follows a modular architecture for maintainability and scalability:
sdk/
├── src/
│ ├── core/ # Core SDK functionality
│ │ └── index.ts # Core module entry point
│ │ # Future: StepBeam, EventQueue, types
│ │
│ ├── modules/ # Feature modules
│ │ ├── index.ts # Modules barrel export
│ │ ├── guides/ # Product guides module
│ │ │ └── index.ts # Guides entry point
│ │ │ # Future: TriggerManager, GuideRenderer, TooltipRenderer,
│ │ │ # ModalRenderer, HotspotRenderer
│ │ │
│ │ └── nps/ # NPS surveys module
│ │ └── index.ts # NPS entry point
│ │ # Future: NPSTriggerManager, NPSSurveyRenderer,
│ │ # SurveyFrequencyManager
│ │
│ ├── utils/ # Shared utilities
│ │ └── index.ts # Utils entry point
│ │ # Future: i18n, TranslationManager, SelectorResolver,
│ │ # selectorUtils, styles
│ │
│ └── index.ts # Main SDK entry point
│
├── dist/ # Built output
├── examples/ # Usage examples
├── package.json
├── tsconfig.json
└── README.mdModule Responsibilities
| Module | Purpose |
|--------|---------|
| core | Main SDK orchestration, event queuing, core types |
| modules/guides | Product tour rendering and trigger management |
| modules/nps | NPS survey rendering and frequency control |
| utils | i18n, selectors, styling, and shared helpers |
Import Patterns
// Main entry point (recommended)
import { StepBeam } from '@stepbeam/sdk';
// Direct module imports (for tree-shaking)
import { GuideRenderer } from '@stepbeam/sdk/modules/guides';
import { NPSSurveyRenderer } from '@stepbeam/sdk/modules/nps';
import { detectBrowserLanguage } from '@stepbeam/sdk/utils';SDK Initialization API
The SDK must be initialized with an API key before any other functions can be used. Initialization authenticates with the StepBeam backend and retrieves your account configuration.
init(apiKey: string, options?: SDKInitOptions): Promise<void>
Initialize the SDK with your API key. This function is async and authenticates with the backend.
import { init } from '@stepbeam/sdk';
// Basic initialization
await init('pk_live_your_api_key_here');
// With options (apiUrl is REQUIRED)
await init('pk_live_your_api_key_here', {
apiUrl: 'https://api.stepbeam.com', // Required!
debug: true,
});
// For local development:
await init('pk_live_your_api_key_here', {
apiUrl: 'http://localhost:3011',
debug: true,
});Parameters:
apiKey(string): Your unique API key from the StepBeam dashboard. Must be at least 16 characters.options(SDKInitOptions): Configuration options:apiUrl(string, required): Your StepBeam API endpoint URL. Must be a valid HTTP/HTTPS URL. There is no default value - this prevents accidental deployments to production with localhost URLs.debug(boolean): Enable debug logging (default: false)
⚠️ Breaking Change (v1.1): The
apiUrloption is now required. Previously, the SDK defaulted tohttp://localhost:3011, which caused issues when deploying to production. You must now explicitly provide your API URL.
Throws:
SDKInitializationError: If the apiKey is invalid (empty, too short, etc.)SDKAuthenticationError: If authentication with the backend failsSDKAlreadyInitializedError: Ifinit()has already been called
getSiteId(): string
Get the site ID returned from authentication.
import { init, getSiteId } from '@stepbeam/sdk';
await init('pk_live_your_api_key_here');
const siteId = getSiteId(); // 'site_abc123'Throws:
SDKNotInitializedError: Ifinit()has not been called
getApiKey(): string
Get the API key used for initialization.
import { init, getApiKey } from '@stepbeam/sdk';
await init('pk_live_your_api_key_here');
const apiKey = getApiKey();getAccountName(): string
Get the account name associated with your API key.
import { init, getAccountName } from '@stepbeam/sdk';
await init('pk_live_your_api_key_here');
const accountName = getAccountName(); // 'Your Company'getApiUrl(): string
Get the configured API URL.
import { init, getApiUrl } from '@stepbeam/sdk';
await init('pk_live_your_api_key_here');
const apiUrl = getApiUrl(); // 'https://api.stepbeam.com'getConfig(): object
Get the full SDK configuration.
import { init, getConfig } from '@stepbeam/sdk';
await init('pk_live_your_api_key_here', { debug: true });
const config = getConfig();
// {
// siteId: 'site_abc123',
// apiUrl: 'https://api.stepbeam.com',
// debug: true,
// accountName: 'Your Company',
// authenticatedAt: '2024-01-15T10:30:00.000Z'
// }isInitialized(): boolean
Check if the SDK has been initialized.
import { init, isInitialized } from '@stepbeam/sdk';
console.log(isInitialized()); // false
await init('pk_live_your_api_key_here');
console.log(isInitialized()); // trueisInitializing(): boolean
Check if SDK initialization is currently in progress.
import { isInitializing } from '@stepbeam/sdk';
console.log(isInitializing()); // true (during init)Error Classes
The SDK provides specific error classes for initialization issues:
import {
init,
SDKInitializationError,
SDKAuthenticationError,
SDKNotInitializedError,
SDKAlreadyInitializedError,
} from '@stepbeam/sdk';
try {
await init('your-api-key');
} catch (error) {
if (error instanceof SDKInitializationError) {
console.error('Invalid API key format:', error.message);
} else if (error instanceof SDKAuthenticationError) {
console.error('Authentication failed:', error.message);
} else if (error instanceof SDKAlreadyInitializedError) {
console.error('SDK already initialized');
}
}| Error Class | When Thrown |
|-------------|-------------|
| SDKInitializationError | Invalid apiKey (empty, too short) |
| SDKAuthenticationError | Authentication with backend failed |
| SDKAlreadyInitializedError | init() called more than once |
| SDKNotInitializedError | SDK function called before init() |
Debug Logging and Error Handling
Enable verbose logging during development to diagnose initialization and runtime issues:
import { init } from '@stepbeam/sdk';
await init('pk_live_your_api_key_here', {
apiUrl: 'https://api.stepbeam.com',
debug: true,
});Or via the script-tag global:
StepBeam('init', {
appKey: 'YOUR_APP_KEY',
apiUrl: 'https://api.stepbeam.com',
debug: true,
});Network failures (event delivery, guide fetches) are retried automatically with
exponential backoff. The retry behavior is internal and not exposed as a
public configuration option — if you need custom error reporting, wrap your own
initialization or track() calls in a try/catch and forward to your monitoring
service.
NPS Surveys
NPS surveys are configured in the StepBeam admin (Surveys → Create NPS) with their own targeting rules — segment, page URL, frequency, cooldown — and auto-render in the SDK whenever the targeting matches and the visitor is eligible per the configured cooldown.
For most customers, no SDK code is needed beyond the standard install: as soon
as init() succeeds and the visitor is identified, eligible NPS surveys are
fetched and shown automatically.
For advanced use cases (rendering an NPS widget at a specific moment in your
app), import the NPSSurveyRenderer class directly:
import { NPSSurveyRenderer } from '@stepbeam/sdk';
const renderer = new NPSSurveyRenderer({
// theme, callbacks, etc.
});
renderer.render(survey); // survey is the NPSSurvey object from the APIThe auto-rendered widget shows the survey title and question, a 0-10 rating scale, an optional description and comment field, and a close button. Submission auto-dismisses the widget and posts the response to the StepBeam backend.
Custom Event Tracking
The SDK lets you send arbitrary analytics events to the backend alongside the built-in guide lifecycle events (guide_started, step_completed, etc.). This is useful for tracking feature usage, button clicks, and any other in-app behaviour you want to correlate with guide engagement.
TypeScript SDK (StepBeam class)
import { StepBeam } from '@stepbeam/sdk';
const guide = new StepBeam({ apiUrl: 'https://api.stepbeam.com' });
// 1. Simple event – just a name
const result = await guide.trackCustomEvent('button_click');
// 2. Event with metadata properties
const result = await guide.trackCustomEvent('feature_used', {
featureName: 'quick_actions',
page: '/settings',
});
// 3. Event with explicit context (guide, step, session, user)
const result = await guide.trackCustomEvent(
'onboarding_step_skipped',
{ reason: 'already_familiar' },
{
guideId: 42,
stepId: 3,
sessionId: 'sess_abc123',
userId: 'usr_xyz789',
}
);
// 4. Check the result
if (!result.ok) {
console.error('Tracking failed:', result.error);
}Browser snippet (StepBeam global)
When the SDK is loaded via the script-tag installer (Option 2), the same
custom-event API is available through the global StepBeam(command, ...)
dispatcher:
// Simple
StepBeam('trackCustomEvent', 'button_click', { buttonId: 'signup' });
// With context and callback
StepBeam(
'trackCustomEvent',
'feature_used',
{ featureName: 'export_csv' },
{ guideId: 5, userId: 'usr_abc' },
function (err, event) {
if (err) console.error(err);
else console.log('Tracked:', event);
}
);Calls made before the SDK finishes loading are queued by the async loader and replayed automatically once it's ready, so you don't need to wait for a "loaded" event before tracking.
Event Name Rules
| Rule | Example |
|---|---|
| Must start with a letter | ✅ button_click ❌ 1click |
| Only letters, digits, underscores | ✅ featureUsed ❌ my-event |
| No spaces or special characters | ❌ my event ❌ com.click |
Metadata Size Limit
The properties object is serialised to JSON before sending. The total serialised size must not exceed 10 KB. Payloads larger than 10 KB are rejected client-side before any network request is made.
How It Works Under the Hood
- The SDK validates the event name and serialised properties size.
- A
POST /eventsrequest is sent withtype: "custom". The event name is placed inmetadata.customEventName; all extra properties are merged intometadata. - The backend persists the event and fires any configured webhooks.
- The event appears in the real-time event stream (
GET /events/stream) and is queryable viaGET /events?type=custom.
Static Validation Helpers
Both validation steps are exposed as static methods for use outside of a StepBeam instance:
import { StepBeam } from '@stepbeam/sdk';
StepBeam.validateEventName('button_click');
// → { valid: true }
StepBeam.validateEventName('123bad');
// → { valid: false, error: '...' }
StepBeam.validateEventProperties({ page: '/home' });
// → { valid: true, serialized: '{"page":"/home"}' }Guide Triggers
Guides can be configured to start automatically based on different trigger conditions. This is particularly useful for contextual onboarding and just-in-time help.
Page Load Trigger
Automatically start a guide when the page loads, with an optional delay:
{
trigger: {
type: 'page_load',
delay: 2000 // milliseconds (optional, defaults to 0)
}
}Use cases:
- Welcome tours for new users
- Feature announcements
- General onboarding flows
Element Visible Trigger
Start a guide when a specific element becomes visible in the viewport:
{
trigger: {
type: 'element_visible',
selector: '#dashboard-widget',
threshold: 0.5 // percentage visible (optional, defaults to 0.5)
}
}Use cases:
- Contextual help for specific features
- Progressive disclosure of functionality
- Feature discovery as users scroll
URL Change Trigger (SPA Support)
Automatically start a guide when the URL matches a specific pattern. Perfect for Single Page Applications (SPAs) built with React, Vue, Angular, etc.
{
trigger: {
type: 'url_change',
urlPattern: '/dashboard*', // Wildcard pattern
triggerOnInit: true // Check on page load (optional, defaults to true)
}
}URL Pattern Formats:
- Wildcard: Use
*for any characters (e.g.,*/products/*,*dashboard*) - Regex: Use
/pattern/flagsformat (e.g.,/\/product\/\d+/,/.*\/settings$/i)
Examples:
// Trigger on any dashboard page
{ type: 'url_change', urlPattern: '*/dashboard*' }
// Trigger on specific product pages using regex
{ type: 'url_change', urlPattern: '/\\/products\\/\\d+/' }
// Trigger on hash changes
{ type: 'url_change', urlPattern: '*#onboarding*' }
// Don't trigger on initial load, only on navigation
{ type: 'url_change', urlPattern: '*/settings*', triggerOnInit: false }Use cases:
- Page-specific onboarding in SPAs
- Feature tours when users navigate to specific sections
- Context-aware help that appears on relevant pages
- Progressive onboarding across multiple SPA routes
Supported Navigation Events:
pushState(React Router, Vue Router navigation)replaceState(programmatic URL updates)popstate(browser back/forward buttons)hashchange(hash-based routing)
Manual Trigger
Require explicit .start() call (default behavior):
{
trigger: {
type: 'manual'
}
}Use cases:
- User-initiated help
- Help menu items
- Custom trigger logic
Disabling Auto-Start
You can load a guide without auto-starting, even if it has a trigger:
await guide.loadGuide('guide-id', false); // autoStart = false
// Guide is loaded but won't start automatically
guide.start(); // Start manually when readyStep Types
Modal Steps
Full-screen modal dialogs that overlay the page content.
{
type: 'modal',
title: 'Welcome!',
content: 'Get started with our product',
config: {
showOverlay: true,
overlayColor: '#000000',
overlayOpacity: 0.5,
primaryButtonText: 'Next',
secondaryButtonText: 'Back',
showCloseButton: true,
position: 'center', // 'center' | 'top' | 'bottom'
width: 'medium' // 'small' | 'medium' | 'large'
}
}Tooltip Steps
Context-aware tooltips that point to specific elements on the page.
{
type: 'tooltip',
title: 'Feature Name',
content: 'This is how you use this feature',
selector: '#feature-button',
config: {
placement: 'top', // 'top' | 'bottom' | 'left' | 'right'
showArrow: true,
highlightElement: true,
primaryButtonText: 'Next',
secondaryButtonText: 'Back'
}
}Hotspot Steps
Interactive hotspots that users can click to reveal more information.
{
type: 'hotspot',
title: 'New Feature',
content: 'Click to learn more about this feature',
selector: '#new-feature',
config: {
pulse: true,
color: '#007bff',
size: 'medium' // 'small' | 'medium' | 'large'
}
}Multi-Language Support
The SDK includes comprehensive internationalization (i18n) support for creating guides in multiple languages with automatic language detection and RTL layout support.
Features
- Automatic Language Detection: Detects user's preferred language from browser settings
- Manual Language Control: Set and change language programmatically
- RTL Layout Support: Automatic right-to-left layout for Arabic and Hebrew
- Translation Fallbacks: Gracefully falls back to default language when translations are missing
- Per-Step Translations: Translate titles, content, and button texts independently
Basic Usage
const guide = new StepBeam({
apiUrl: 'https://api.stepbeam.com',
preferredLanguage: 'es', // Optional: Set preferred language
autoDetectLanguage: true, // Optional: Auto-detect from browser (default: true)
onLanguageChange: (language) => {
console.log('Language changed to:', language);
}
});
// Load guide with translations
await guide.loadGuide('welcome-tour-multilingual');
guide.start();Changing Language
// Get current language
const currentLanguage = guide.getCurrentLanguage(); // e.g., 'en'
// Change language
guide.setLanguage('es'); // Switches to Spanish and re-renders current step
// Get available languages for current guide
const languages = guide.getAvailableLanguages(); // e.g., ['en', 'es', 'fr', 'de']Translation Structure
Guide-Level Translations
Define translations for your guide's title and description:
{
"id": "welcome-tour",
"title": "Welcome Tour",
"description": "Learn how to use our product",
"defaultLanguage": "en",
"availableLanguages": ["en", "es", "fr", "de"],
"translations": [
{
"language": "es",
"title": "Tour de Bienvenida",
"description": "Aprende a usar nuestro producto"
},
{
"language": "fr",
"title": "Visite de Bienvenue",
"description": "Apprenez à utiliser notre produit"
}
]
}Step-Level Translations
Translate individual step content and button texts:
{
"type": "modal",
"title": "Welcome!",
"content": "<p>Get started with our product</p>",
"translations": [
{
"language": "es",
"title": "¡Bienvenido!",
"content": "<p>Comienza con nuestro producto</p>",
"primaryButtonText": "Siguiente",
"secondaryButtonText": "Atrás"
},
{
"language": "fr",
"title": "Bienvenue !",
"content": "<p>Commencez avec notre produit</p>",
"primaryButtonText": "Suivant",
"secondaryButtonText": "Retour"
}
],
"config": {
"primaryButtonText": "Next",
"secondaryButtonText": "Back"
}
}RTL (Right-to-Left) Support
The SDK automatically handles RTL layouts for Arabic (ar) and Hebrew (he):
- Text alignment is automatically reversed
- Close buttons and UI elements are mirrored
- Button order is reversed
dir="rtl"is applied to the HTML element
// Switch to Arabic (automatically enables RTL)
guide.setLanguage('ar');Creating a Language Switcher
Example language switcher component:
<select id="language-switcher">
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
<option value="de">Deutsch</option>
<option value="ar">العربية</option>
<option value="he">עברית</option>
</select>
<script>
const guide = new StepBeam({
apiUrl: 'https://api.stepbeam.com',
onLanguageChange: (lang) => {
document.getElementById('language-switcher').value = lang;
}
});
document.getElementById('language-switcher').addEventListener('change', (e) => {
guide.setLanguage(e.target.value);
});
// Load and start guide
await guide.loadGuide('welcome-tour-multilingual');
guide.start();
// Set initial language in dropdown
document.getElementById('language-switcher').value = guide.getCurrentLanguage();
</script>Translation Best Practices
- Set Default Language: Always specify a
defaultLanguagein your guide - Provide Fallbacks: Include English translations as a fallback
- Test RTL Layouts: Test your guides with Arabic or Hebrew to ensure proper RTL rendering
- Keep Translations Consistent: Use the same language codes across all steps
- Translate All User-Facing Text: Don't forget button labels and tooltips
- Use Professional Translators: For production, use professional translation services
Supported Language Codes
The SDK supports the following ISO 639-1 language codes:
en- Englishes- Spanishfr- Frenchde- Germanit- Italianpt- Portuguesear- Arabic (RTL)he- Hebrew (RTL)ja- Japaneseko- Koreanzh- Chineseru- Russian
Example Multilingual Guide
See the complete example in sdk/examples/multilingual-guide.json for a fully translated guide with English, Spanish, French, and German translations.
API
Constructor
new StepBeam(config: StepBeamConfig)Config Options:
apiUrl(required): Base URL for the guides APIapplicationId(required): Application ID for authenticationappKey(required): Application key for authenticationuserId(required): User ID for identifying the guide viewer - persisted with all eventsuserProperties(optional): Object containing user context (e.g.,{ plan: 'pro', country: 'US', role: 'admin' }) - enriches all tracked eventsapiKey(optional): API key for additional authenticationsessionId(optional): Session ID for event tracking (auto-generated if not provided)trackEvents(optional): Whether to track events (default: true)onStepComplete(optional): Callback when a step is completedonGuideComplete(optional): Callback when the entire guide is completedonGuideClose(optional): Callback when the guide is closed
Methods
loadGuide(guideId: string, autoStart?: boolean): Promise<void>
Load a guide from the API. If autoStart is true (default) and the guide has a trigger configuration, the trigger will be set up automatically.
start(): void
Start the loaded guide.
pause(): void
Pause the current guide.
resume(): void
Resume a paused guide.
stop(): void
Stop the guide and clean up.
next(): void
Move to the next step.
previous(): void
Move to the previous step.
getCurrentStep(): Step | null
Get the current step.
getState(): GuideState
Get the current state of the guide.
isActive(): boolean
Check if a guide is currently active.
getCurrentLanguage(): LanguageCode
Get the current active language.
setLanguage(language: LanguageCode, reRender?: boolean): void
Set the current language and optionally re-render the current step.
getAvailableLanguages(): LanguageCode[] | null
Get available languages for the current guide.
trackCustomEvent(name: string, properties?: Record<string, unknown>, context?: CustomEventContext): Promise<CustomEventResult>
Track a custom analytics event. name must match /^[a-zA-Z][a-zA-Z0-9_]*$/. properties are merged into the event metadata (max 10 KB serialised). context optionally supplies guideId, stepId, sessionId, and userId.
static validateEventName(name: unknown): { valid: true } | { valid: false; error: string }
Validate a custom event name without sending anything.
static validateEventProperties(properties: unknown): { valid: true; serialized: string } | { valid: false; error: string }
Validate custom event properties without sending anything.
Data Model
Guides are structured as follows:
interface Guide {
id: string;
title: string;
description: string;
sections: Section[];
trigger?: Trigger; // Optional trigger configuration
}
type Trigger = PageLoadTrigger | ElementVisibleTrigger | ManualTrigger;
interface PageLoadTrigger {
type: 'page_load';
delay?: number; // milliseconds
}
interface ElementVisibleTrigger {
type: 'element_visible';
selector: string;
threshold?: number; // 0-1 (percentage of element visible)
}
interface ManualTrigger {
type: 'manual';
}
interface Section {
id: string;
title: string;
steps: Step[];
}
type Step = ModalStep | TooltipStep | HotspotStep;Development
# Install dependencies
npm install
# Run tests
npm test
# Build
npm run buildTesting
The SDK includes comprehensive unit tests using Vitest:
npm testBrowser Support
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- Mobile browsers (iOS Safari, Chrome Mobile)
License
MIT
