npm package: @harmonixit/harmonix-everywhere
Description: A lightweight library for integrating with Harmonix Chrome extension
Latest version: 1.2.1

# 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
```bash
npm install @harmonixit/harmonix-everywhere
```

### Package Info
- **Package**: `@harmonixit/harmonix-everywhere`
- **Version**: 1.2.1
- **Main**: `dist/index.js`
- **Module**: `dist/index.esm.js`
- **Types**: `dist/index.d.ts`

## Quick Start

```javascript
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

```javascript
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

```javascript
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

```javascript
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

```javascript
// 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:
```html
<!-- 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:**
```typescript
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:**
```javascript
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:**
```javascript
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: `30`
- `retryInterval` (number, optional): Interval between retry attempts in milliseconds. Default: `1000`

**Returns:** `Promise<boolean>` - True if initialization was successful

**Example:**
```javascript
// 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 system
- `objectType` ('Contact' | 'Company'): Type of object to open
- `objectData` (ContactInfo | CompanyInfo): Contact or company information

**Returns:** `Promise<{success: boolean, objectId?: string}>` - Result with success status and Harmonix object ID

**Contact Data Fields (ContactInfo):**
```typescript
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):**
```typescript
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:**

```javascript
// 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 send
- `params` (any, optional): Parameters for the command

**Returns:** `Promise<T>` - The response from the extension

**Example:**
```javascript
// 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:

```javascript
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:

```typescript
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

```bash
# Install dependencies
npm install

# Build the library first
npm run build

# Start the development server
npm run dev
```

Then open your browser to `http://localhost:3001` to view the examples.

### Included Examples

1. **Basic Usage Example** (`examples/index.html`)
   - Simple form to open contacts in Harmonix
   - Extension availability checking
   - Basic error handling
   
2. **CRM Dashboard** (`examples/crm-example/index.html`)
   - Complete CRM interface mockup
   - Auto-initialization pattern
   - Status indicators for extension availability
   
3. **Contact Detail Page** (`examples/crm-example/lead-page.html`)
   - Real-world contact page integration
   - Data extraction from page DOM
   - Complete contact information handling
   
4. **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:

```javascript
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:

```javascript
// 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:

```javascript
// 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 interface
```

### 4. Unique Object IDs

Always use unique, stable identifiers for your objects:

```javascript
// 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:

```javascript
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:

```javascript
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

```jsx
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

```vue
<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 `maxRetries` and `retryInterval` for 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_name` for contacts, `name` for companies)
- Check that data is properly formatted
- Use debug mode to see detailed logs

### Debug Mode

Enable debug mode to see detailed logging:

```javascript
const harmonix = new Harmonix({ debug: true });
```

This will log all communication with the extension to the browser console.

## Development

### Building the Library

```bash
# Install dependencies
npm install

# Build the library
npm run build

# Watch for changes during development
npm run watch
```

### Testing

```bash
# Run tests
npm test

# Lint the code
npm run lint
```

### Directory 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 files
```

## Support

For issues, questions, or contributions:

1. Check the examples in the `examples/` directory
2. Enable debug mode for detailed logging
3. Refer to the API documentation above
4. Contact the development team for additional support

## License

MIT