@harmonixit/harmonix-everywhere v1.1.9
Harmonix Everywhere
A lightweight JavaScript library for integrating with the Harmonix Chrome extension. This library provides a simple interface to communicate with the Harmonix extension and open contacts or companies directly from your web application.
Installation
NPM
npm install @harmonixit/harmonix-everywherePackage Info
- Package:
@harmonixit/harmonix-everywhere - Version: 1.1.8
- Main:
dist/index.js - Module:
dist/index.esm.js - Types:
dist/index.d.ts
Quick Start
import { Harmonix } from '@harmonixit/harmonix-everywhere';
// Create and initialize
const harmonix = new Harmonix({ debug: true });
// Check extension availability and open a contact
const openContact = async () => {
const isAvailable = await harmonix.isExtensionAvailable();
if (isAvailable) {
await harmonix.initialize();
const result = await harmonix.openContact('contact-123', 'Contact', {
first_name: 'John',
last_name: 'Doe',
email: 'john@example.com'
});
console.log('Contact opened:', result);
} else {
console.log('Harmonix extension not available');
}
};Usage Examples
1. Basic Contact Opening
import { Harmonix } from '@harmonixit/harmonix-everywhere';
// Create a Harmonix client
const harmonix = new Harmonix({ debug: true });
// Initialize the client with default retry settings (30 attempts, 1 second interval)
harmonix.initialize().then(isInitialized => {
if (isInitialized) {
console.log('Harmonix extension is available and initialized');
// Open a contact in the extension
harmonix.openContact(
'ext-123', // External ID for the contact
'Contact', // Object type ('Contact' or 'Company')
{
first_name: 'John',
last_name: 'Doe',
email: 'john@example.com',
phone_number: '+1234567890',
job_role: 'Sales Manager',
company_name: 'ACME Inc'
}
)
.then(result => {
console.log('Contact opened:', result);
})
.catch(error => {
console.error('Failed to open contact:', error);
});
} else {
console.log('Harmonix extension is not available');
}
});2. Auto-Initialization Pattern
import { Harmonix } from '@harmonixit/harmonix-everywhere';
// Create a Harmonix client with auto-initialization
const harmonix = new Harmonix({
debug: true,
autoInitialize: true, // Start initialization immediately
maxRetries: 30, // Try for 30 seconds
retryInterval: 1000 // Check every second
});
// The initialization process is already running in the background
// Later, when you need to use the extension, you can just call your methods
// The library will ensure initialization is complete before proceeding
// Example: When the user clicks a button to open a contact
document.getElementById('openContactButton').addEventListener('click', async () => {
try {
const result = await harmonix.openContact(
'ext-123',
'Contact',
{
first_name: 'John',
last_name: 'Doe',
email: 'john@example.com'
}
);
console.log('Contact opened:', result);
} catch (error) {
console.error('Failed to open contact:', error);
}
});3. Company Opening
import { Harmonix } from '@harmonixit/harmonix-everywhere';
const harmonix = new Harmonix({ debug: true, autoInitialize: true });
// Open a company in Harmonix
const openCompany = async () => {
try {
const result = await harmonix.openContact(
'company-techcorp',
'Company',
{
name: 'TechCorp',
industry: 'Enterprise Software',
size: '2,500+ employees',
revenue: '$500M - $1B',
phone_number: '+12125551234',
email: 'info@techcorp.com',
linkedin_url: 'https://linkedin.com/company/techcorp',
website: 'https://techcorp.com',
city: 'New York',
country: 'United States',
address: '555 Tech Avenue, Floor 32'
}
);
if (result.success) {
console.log('Company opened successfully:', result.objectId);
}
} catch (error) {
console.error('Failed to open company:', error);
}
};4. CRM Integration Example
// Real-world CRM integration example
import { Harmonix } from '@harmonixit/harmonix-everywhere';
class CRMHarmonixIntegration {
constructor() {
this.harmonix = new Harmonix({
debug: true,
autoInitialize: true,
maxRetries: 30,
retryInterval: 1000
});
this.setupEventListeners();
}
async setupEventListeners() {
// Wait for DOM to be ready
document.addEventListener('DOMContentLoaded', async () => {
// Check extension availability
const isAvailable = await this.harmonix.isExtensionAvailable();
if (isAvailable) {
// Enable Harmonix buttons
this.enableHarmonixButtons();
} else {
// Disable and show unavailable state
this.disableHarmonixButtons();
}
});
}
enableHarmonixButtons() {
const buttons = document.querySelectorAll('[data-harmonix-action]');
buttons.forEach(button => {
button.disabled = false;
button.addEventListener('click', this.handleHarmonixAction.bind(this));
});
}
disableHarmonixButtons() {
const buttons = document.querySelectorAll('[data-harmonix-action]');
buttons.forEach(button => {
button.disabled = true;
button.title = 'Harmonix extension not available';
});
}
async handleHarmonixAction(event) {
const button = event.target;
const action = button.dataset.harmonixAction;
const objectId = button.dataset.objectId;
const objectType = button.dataset.objectType;
try {
// Show loading state
button.textContent = 'Opening in Harmonix...';
button.disabled = true;
// Extract data based on object type
const objectData = this.extractObjectData(objectType);
// Open in Harmonix
const result = await this.harmonix.openContact(objectId, objectType, objectData);
if (result.success) {
// Show success state temporarily
button.textContent = 'Opened Successfully!';
setTimeout(() => {
button.textContent = 'Open in Harmonix';
button.disabled = false;
}, 2000);
}
} catch (error) {
console.error('Error opening in Harmonix:', error);
button.textContent = 'Error - Try Again';
setTimeout(() => {
button.textContent = 'Open in Harmonix';
button.disabled = false;
}, 2000);
}
}
extractObjectData(objectType) {
if (objectType === 'Contact') {
return this.extractContactDataFromPage();
} else if (objectType === 'Company') {
return this.extractCompanyDataFromPage();
}
}
extractContactDataFromPage() {
// Extract contact information from page DOM
return {
first_name: document.querySelector('[data-field="firstName"]')?.textContent || 'Unknown',
last_name: document.querySelector('[data-field="lastName"]')?.textContent || 'Unknown',
email: document.querySelector('[data-field="email"]')?.textContent,
phone_number: document.querySelector('[data-field="phone"]')?.textContent,
job_role: document.querySelector('[data-field="jobRole"]')?.textContent,
company_name: document.querySelector('[data-field="company"]')?.textContent,
linkedin_url: document.querySelector('[data-field="linkedin"]')?.href
};
}
extractCompanyDataFromPage() {
// Extract company information from page DOM
return {
name: document.querySelector('[data-field="companyName"]')?.textContent || 'Unknown',
industry: document.querySelector('[data-field="industry"]')?.textContent,
size: document.querySelector('[data-field="size"]')?.textContent,
email: document.querySelector('[data-field="email"]')?.textContent,
phone_number: document.querySelector('[data-field="phone"]')?.textContent,
website: document.querySelector('[data-field="website"]')?.href,
linkedin_url: document.querySelector('[data-field="linkedin"]')?.href
};
}
}
// Initialize the integration
new CRMHarmonixIntegration();HTML for the CRM integration:
<!-- Contact page -->
<div class="contact-details">
<h1><span data-field="firstName">Sarah</span> <span data-field="lastName">Johnson</span></h1>
<p data-field="jobRole">Marketing Director</p>
<p data-field="company">Acme Inc.</p>
<a href="mailto:sarah@acme.com" data-field="email">sarah@acme.com</a>
<button
data-harmonix-action="open"
data-object-id="contact-sarah-johnson"
data-object-type="Contact"
class="btn-primary">
Open in Harmonix
</button>
</div>
<!-- Company page -->
<div class="company-details">
<h1 data-field="companyName">TechCorp</h1>
<p data-field="industry">Enterprise Software</p>
<p data-field="size">2,500+ employees</p>
<a href="https://techcorp.com" data-field="website">techcorp.com</a>
<button
data-harmonix-action="open"
data-object-id="company-techcorp"
data-object-type="Company"
class="btn-primary">
Open in Harmonix
</button>
</div>API Reference
Constructor
new Harmonix(options)
Creates a new instance of the Harmonix client.
Options:
interface HarmonixOptions {
debug?: boolean; // Enables debug logging. Default: false
autoInitialize?: boolean; // Automatically initialize upon creation. Default: false
maxRetries?: number; // Maximum retry attempts when initializing. Default: 30
retryInterval?: number; // Interval between retries in ms. Default: 1000
mainSelector?: string; // Main selector for the extension. Default: 'body'
headerHeight?: number; // Header height in pixels. Default: 0
}Example:
const harmonix = new Harmonix({
debug: true,
autoInitialize: true,
maxRetries: 30,
retryInterval: 1000
});Core Methods
harmonix.isExtensionAvailable()
Checks if the Harmonix extension is installed and available.
Returns: Promise<boolean> - True if extension is available
Example:
const isAvailable = await harmonix.isExtensionAvailable();
if (isAvailable) {
console.log('Extension is ready to use');
} else {
console.log('Please install the Harmonix extension');
}harmonix.initialize(maxRetries?, retryInterval?)
Initializes the Harmonix client and establishes a connection with the extension. The method will retry connecting to the extension if it's not immediately available.
Parameters:
maxRetries(number, optional): Maximum number of retry attempts. Default:30retryInterval(number, optional): Interval between retry attempts in milliseconds. Default:1000
Returns: Promise<boolean> - True if initialization was successful
Example:
// Initialize with default settings (30 retries, 1 second intervals)
const success = await harmonix.initialize();
// Initialize with custom retry settings
const success = await harmonix.initialize(60, 500); // 60 attempts, 500ms intervals
if (success) {
console.log('Successfully connected to Harmonix extension');
} else {
console.log('Failed to connect to Harmonix extension');
}harmonix.openContact(objectId, objectType, objectData)
Opens a contact or company in the Harmonix extension. This is the main method for integrating with Harmonix.
Parameters:
objectId(string): Unique identifier for the object in your systemobjectType('Contact' | 'Company'): Type of object to openobjectData(ContactInfo | CompanyInfo): Contact or company information
Returns: Promise<{success: boolean, objectId?: string}> - Result with success status and Harmonix object ID
Contact Data Fields (ContactInfo):
interface ContactInfo {
first_name: string; // Required: Contact's first name
last_name: string; // Required: Contact's last name
email?: string; // Primary email address
secondary_email?: string; // Secondary email address
phone_number?: string; // Primary phone number
secondary_phone_number?: string; // Secondary phone number
job_role?: string; // Contact's job role or title
company_name?: string; // Company name where the contact works
linkedin_url?: string; // LinkedIn profile URL
address?: string; // Physical address
city?: string; // City
country?: string; // Country
website?: string; // Website URL
assigned_to?: string; // User id or email to assign the object to
[key: string]: any; // Additional custom fields
}Company Data Fields (CompanyInfo):
interface CompanyInfo {
name: string; // Required: Company name
industry?: string; // Industry or sector
size?: string; // Company size (number of employees)
revenue?: string; // Annual revenue
email?: string; // Primary email address
phone_number?: string; // Primary phone number
linkedin_url?: string; // LinkedIn profile URL
address?: string; // Physical address
city?: string; // City
country?: string; // Country
website?: string; // Website URL
assigned_to?: string; // User id or email to assign the object to
[key: string]: any; // Additional custom fields
}Examples:
// Open a contact
const contactResult = await harmonix.openContact(
'crm-contact-123',
'Contact',
{
first_name: 'Sarah',
last_name: 'Johnson',
email: 'sarah.johnson@acme.com',
phone_number: '+15551234567',
job_role: 'Marketing Director',
company_name: 'Acme Inc.',
linkedin_url: 'https://linkedin.com/in/sarahjohnson'
}
);
if (contactResult.success) {
console.log('Contact opened with ID:', contactResult.objectId);
}
// Open a company
const companyResult = await harmonix.openContact(
'crm-company-456',
'Company',
{
name: 'TechCorp',
industry: 'Enterprise Software',
size: '2,500+ employees',
email: 'info@techcorp.com',
website: 'https://techcorp.com',
linkedin_url: 'https://linkedin.com/company/techcorp'
}
);
if (companyResult.success) {
console.log('Company opened with ID:', companyResult.objectId);
}harmonix.sendCommand(command, params?)
Sends a custom command to the Harmonix extension. This is a lower-level method that allows for custom integrations.
Parameters:
command(string): The command to sendparams(any, optional): Parameters for the command
Returns: Promise<T> - The response from the extension
Example:
// Send a custom command
const result = await harmonix.sendCommand('customAction', {
data: 'example'
});
console.log('Command result:', result);Error Handling
The library uses standard JavaScript Promises and will throw errors for various failure conditions:
try {
const result = await harmonix.openContact('id', 'Contact', {
first_name: 'John',
last_name: 'Doe'
});
if (result.success) {
console.log('Success!');
}
} catch (error) {
if (error.message.includes('not available')) {
console.log('Extension not installed or disabled');
} else if (error.message.includes('requires')) {
console.log('Missing required fields');
} else {
console.log('Unexpected error:', error.message);
}
}Type Definitions
The library exports TypeScript type definitions for better development experience:
import { Harmonix, HarmonixOptions, ContactInfo, CompanyInfo, ObjectType } from '@harmonixit/harmonix-everywhere';Working Examples
The package includes a set of comprehensive example implementations showing how to integrate the Harmonix library in different scenarios.
Running the Examples
# Install dependencies
npm install
# Build the library first
npm run build
# Start the development server
npm run devThen open your browser to http://localhost:3001 to view the examples.
Included Examples
Basic Usage Example (
examples/index.html)- Simple form to open contacts in Harmonix
- Extension availability checking
- Basic error handling
CRM Dashboard (
examples/crm-example/index.html)- Complete CRM interface mockup
- Auto-initialization pattern
- Status indicators for extension availability
Contact Detail Page (
examples/crm-example/lead-page.html)- Real-world contact page integration
- Data extraction from page DOM
- Complete contact information handling
Company Detail Page (
examples/crm-example/company-page.html)- Company-specific integration example
- Advanced data mapping
- Button state management
Note: You need to have the Harmonix Chrome extension installed in your browser for the examples to work properly.
Best Practices
1. Extension Availability Checking
Always check if the extension is available before attempting to use it:
const harmonix = new Harmonix({ debug: true });
// Check availability first
const isAvailable = await harmonix.isExtensionAvailable();
if (!isAvailable) {
// Show user-friendly message
showExtensionNotAvailableMessage();
return;
}2. Graceful Degradation
Design your interface to work even when the extension is not available:
// Enable/disable Harmonix features based on availability
function updateUI(extensionAvailable) {
const harmonixButtons = document.querySelectorAll('.harmonix-action');
harmonixButtons.forEach(button => {
if (extensionAvailable) {
button.disabled = false;
button.textContent = 'Open in Harmonix';
} else {
button.disabled = true;
button.textContent = 'Extension Required';
button.title = 'Please install the Harmonix Chrome extension';
}
});
}3. Auto-Initialization for Better UX
Use auto-initialization to prepare the connection in the background:
// Initialize immediately when the page loads
const harmonix = new Harmonix({
autoInitialize: true,
maxRetries: 30,
retryInterval: 1000
});
// The extension will be ready when users interact with your interface4. Unique Object IDs
Always use unique, stable identifiers for your objects:
// Good: Use your system's unique identifiers
const objectId = `crm-contact-${contact.id}`;
// Bad: Using timestamps or random values
const objectId = `contact-${Date.now()}`;5. Error Handling
Implement comprehensive error handling:
async function openInHarmonix(objectId, objectType, data) {
try {
const result = await harmonix.openContact(objectId, objectType, data);
if (result.success) {
showSuccessMessage(`${objectType} opened successfully`);
} else {
showErrorMessage('Failed to open in Harmonix');
}
} catch (error) {
if (error.message.includes('not available')) {
showInstallPrompt();
} else if (error.message.includes('requires')) {
showValidationError(error.message);
} else {
showGenericError('Something went wrong. Please try again.');
}
}
}6. Data Validation
Validate required fields before sending to Harmonix:
function validateContactData(data) {
const errors = [];
if (!data.first_name) errors.push('First name is required');
if (!data.last_name) errors.push('Last name is required');
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join(', ')}`);
}
}
function validateCompanyData(data) {
if (!data.name) {
throw new Error('Company name is required');
}
}Integration Patterns
React Integration
import React, { useState, useEffect } from 'react';
import { Harmonix } from '@harmonixit/harmonix-everywhere';
function ContactCard({ contact }) {
const [harmonix] = useState(() => new Harmonix({
debug: process.env.NODE_ENV === 'development',
autoInitialize: true
}));
const [extensionAvailable, setExtensionAvailable] = useState(false);
const [loading, setLoading] = useState(false);
useEffect(() => {
harmonix.isExtensionAvailable().then(setExtensionAvailable);
}, [harmonix]);
const handleOpenInHarmonix = async () => {
setLoading(true);
try {
const result = await harmonix.openContact(
`contact-${contact.id}`,
'Contact',
{
first_name: contact.firstName,
last_name: contact.lastName,
email: contact.email,
phone_number: contact.phone,
job_role: contact.jobTitle,
company_name: contact.company
}
);
if (result.success) {
console.log('Contact opened successfully');
}
} catch (error) {
console.error('Failed to open contact:', error);
} finally {
setLoading(false);
}
};
return (
<div className="contact-card">
<h3>{contact.firstName} {contact.lastName}</h3>
<p>{contact.jobTitle} at {contact.company}</p>
<button
onClick={handleOpenInHarmonix}
disabled={!extensionAvailable || loading}
className="harmonix-button"
>
{loading ? 'Opening...' : 'Open in Harmonix'}
</button>
{!extensionAvailable && (
<p className="extension-warning">
Harmonix extension required
</p>
)}
</div>
);
}Vue.js Integration
<template>
<div class="contact-card">
<h3>{{ contact.firstName }} {{ contact.lastName }}</h3>
<p>{{ contact.jobTitle }} at {{ contact.company }}</p>
<button
@click="openInHarmonix"
:disabled="!extensionAvailable || loading"
class="harmonix-button"
>
{{ loading ? 'Opening...' : 'Open in Harmonix' }}
</button>
<p v-if="!extensionAvailable" class="extension-warning">
Harmonix extension required
</p>
</div>
</template>
<script>
import { Harmonix } from '@harmonixit/harmonix-everywhere';
export default {
name: 'ContactCard',
props: ['contact'],
data() {
return {
harmonix: new Harmonix({
debug: process.env.NODE_ENV === 'development',
autoInitialize: true
}),
extensionAvailable: false,
loading: false
};
},
async mounted() {
this.extensionAvailable = await this.harmonix.isExtensionAvailable();
},
methods: {
async openInHarmonix() {
this.loading = true;
try {
const result = await this.harmonix.openContact(
`contact-${this.contact.id}`,
'Contact',
{
first_name: this.contact.firstName,
last_name: this.contact.lastName,
email: this.contact.email,
phone_number: this.contact.phone,
job_role: this.contact.jobTitle,
company_name: this.contact.company
}
);
if (result.success) {
console.log('Contact opened successfully');
}
} catch (error) {
console.error('Failed to open contact:', error);
} finally {
this.loading = false;
}
}
}
};
</script>Troubleshooting
Common Issues
1. Extension not detected
- Ensure the Harmonix Chrome extension is installed and enabled
- Check that the extension has necessary permissions
- Try refreshing the page
2. Initialization timeout
- Increase
maxRetriesandretryIntervalfor slower connections - Check browser console for error messages
- Verify extension is running and accessible
3. Data not appearing in Harmonix
- Ensure required fields are provided (
first_name/last_namefor contacts,namefor companies) - Check that data is properly formatted
- Use debug mode to see detailed logs
Debug Mode
Enable debug mode to see detailed logging:
const harmonix = new Harmonix({ debug: true });This will log all communication with the extension to the browser console.
Development
Building the Library
# Install dependencies
npm install
# Build the library
npm run build
# Watch for changes during development
npm run watchTesting
# Run tests
npm test
# Lint the code
npm run lintDirectory Structure
harmonix-everywhere/
├── src/
│ ├── harmonix.ts # Main Harmonix class
│ ├── types.ts # TypeScript type definitions
│ └── index.ts # Library entry point
├── examples/ # Example implementations
│ ├── basic-usage.js # Simple example
│ ├── index.html # Basic example page
│ └── crm-example/ # Advanced CRM examples
├── dist/ # Built library files
└── tests/ # Test filesSupport
For issues, questions, or contributions:
- Check the examples in the
examples/directory - Enable debug mode for detailed logging
- Refer to the API documentation above
- Contact the development team for additional support
License
MIT