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 🙏

© 2026 – Pkg Stats / Ryan Hefner

harmony-web-tracker

v1.0.0

Published

Harmony JavaScript web analytics and event tracking library

Downloads

2

Readme

Harmony Web Events Tracker

A comprehensive JavaScript tracking library for web analytics and event tracking with extensive third-party integrations. This library provides unified analytics insights by tracking user interactions, page views, custom events, and integrating with various marketing and sales platforms.

🏗️ Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                         Website Page                             │
├─────────────────────────────────────────────────────────────────┤
│  ┌────────────────┐                                             │
│  │   harmony.js   │ ← Main tracking script                      │
│  │                │   • Event collection & batching             │
│  │                │   • User identification                     │
│  │                │   • Session management                      │
│  │                │   • Auto-loads integrations                 │
│  └────────┬───────┘                                             │
│           │                                                      │
│           ├── Exposes Global APIs:                              │
│           │   • Harmony.identify()                              │
│           │   • Harmony.goal()                                  │
│           │   • Harmony.addSharedProperty()                     │
│           │                                                      │
│  ┌────────▼──────────────────────────────────┐                  │
│  │    Auto-Loaded Integration Scripts        │                  │
│  ├────────────────────────────────────────────┤                  │
│  │ Detected & loaded when conditions are met │                  │
│  │ • Arcade     • Drift      • Pardot        │                  │
│  │ • HeyFlow    • Marketo    • Qualified     │                  │
│  │ • Calendly   • Navattic   • ZoomInfo      │                  │
│  └────────────────────────────────────────────┘                  │
│                                                                  │
│  ┌────────────────────────────────────────────┐                  │
│  │    Manual Integration Scripts              │                  │
│  ├────────────────────────────────────────────┤                  │
│  │ Must be explicitly included on page        │                  │
│  │ • harmony-enrich.js → IP enrichment       │                  │
│  │ • harmony-6sense.js → Intent data         │                  │
│  │ • harmony-demandbase.js → ABM data        │                  │
│  │ • harmony-rollworks.js → Ad tracking      │                  │
│  │ • harmony-vector.js → Visitor ID          │                  │
│  └────────────────────────────────────────────┘                  │
└─────────────────────────────────────────────────────────────────┘
                              ↓
                    Backend Server (Porter)
            https://main-11072-06dd6b79-2kf5jph9.onporter.run

🚀 Quick Start

Basic Setup

<script src="harmony.js" 
        data-apikey="YOUR_API_KEY"
        data-server-url="https://main-11072-06dd6b79-2kf5jph9.onporter.run">
</script>

With IP Enrichment

<script src="harmony.js" data-apikey="YOUR_API_KEY"></script>
<script src="harmony-enrich.js" data-apikey="YOUR_API_KEY"></script>

With Manual Integrations

<script src="harmony.js" data-apikey="YOUR_API_KEY"></script>
<script src="harmony-6sense.js"></script>
<script src="harmony-demandbase.js"></script>
<script src="harmony-vector.js"></script>

🔄 How Scripts Interact

1. Main Script (harmony.js)

The core tracking script that:

  • Initializes on page load
  • Creates global Harmony object with public APIs
  • Tracks page views, clicks, scrolls, form submissions
  • Auto-detects and dynamically loads integration scripts
  • Batches and sends events to backend
  • Manages user sessions and identification

2. Integration Loading Process

// harmony.js automatically detects integrations like this:
const integrations = [
  {
    name: 'drift',
    check: () => !!document.querySelector('script[src*="js.driftt.com"]'),
    script: 'harmony-drift.min.js'
  },
  {
    name: 'marketo',
    check: () => !!window.MktoForms2,
    script: 'harmony-marketo-forms.min.js'
  }
  // ... more integrations
];

// Scans page periodically and loads matching integrations

3. Data Flow Back to Main Script

Integration scripts extract data and feed it back:

// Example from harmony-drift.js
if (email) {
  window.Harmony.identify(email);
}

// Example from harmony-6sense.js
window.Harmony.addSharedProperty({
  key: 'company_id',
  value: companyData.company_id,
  properties: companyData
});

// Example from harmony-marketo.js
window.Harmony.goal('Marketo Form Submitted');

📊 Data Flow Examples

Example 1: Visitor Identification via Drift Chat

1. Main script detects Drift script on page
2. Loads harmony-drift.js automatically
3. Drift integration monitors chat events
4. When visitor provides email in chat:
   → harmony-drift.js calls Harmony.identify(email)
   → Main script updates visitor identity
   → All future events include this identity
   → Sends updated data to backend

Example 2: Company Enrichment via IP

1. harmony-enrich.js included on page
2. Makes POST request to /send-enrich endpoint
3. Server identifies company from visitor's IP address
4. Returns enrichment data (if available)
5. Script calls Harmony.addSharedProperty()
6. All future events include company data

Example 3: Intent Data from 6sense

1. harmony-6sense.js manually included on page
2. Checks localStorage for _6senseCompanyDetails
3. Extracts company and intent data
4. Calls Harmony.addSharedProperty() with:
   - Company firmographics
   - Buying stage
   - Intent scores
5. Data attached to all visitor events

🔌 Integration Capabilities

Auto-Loaded Integrations

These are automatically detected and loaded when their conditions are met:

| Integration | Detection Method | Purpose | Data Collected | |------------|-----------------|---------|----------------| | Arcade | iframe[src*="demo.arcade.software"] | Demo tracking | Demo progress, completion events | | Calendly | URL parameter event_type_uuid | Meeting booking | Email, meeting details | | Drift | script[src*="js.driftt.com"] | Live chat | Email, conversation events | | Gravity Forms | WordPress forms | Form tracking | Form submissions, email | | HeyFlow | script[src*="heyflow.app"] | Interactive content | Form data, engagement | | Intercom | Intercom widget | Customer messaging | Visitor ID, email | | Marketo | window.MktoForms2 | Marketing automation | Form submissions | | Navattic | script[src*="js.navattic.com"] | Product demos | Demo interactions | | Pardot | script[src*="pi.pardot.com"] | B2B marketing | Form data, email | | Qualified | script[src*="qualified"] | Conversational marketing | Chat interactions | | ZoomInfo Chat | script[src*="insent.ai"] | B2B chat | Chat events, lead data |

Manual Integrations

These must be explicitly included on your page:

| Integration | Script | Purpose | Data Collected | |------------|--------|---------|----------------| | 6sense | harmony-6sense.js | Intent data platform | Company info, buying stage, intent scores | | Albacross | harmony-albacross.js | Lead generation | Company identification from IP | | Demandbase | harmony-demandbase.js | ABM platform | Company firmographics | | Enrich | harmony-enrich.js | IP enrichment | Company data from IP lookup | | Leadoo | harmony-leadoo.js | Lead conversion | Form submissions | | RollWorks | harmony-rollworks.js | ABM advertising | Account data, ad interactions | | Vector | harmony-vector.js | Visitor tracking | Unique visitor ID |

🔧 Configuration Options

Script Tag Attributes

| Attribute | Required | Description | Example | |-----------|----------|-------------|---------| | data-apikey | ✅ | Your API key for authentication | data-apikey="abc123" | | data-server-url | ❌ | Custom server endpoint | data-server-url="https://your-server.com" | | data-fingerprint-apikey | ❌ | Enable Fingerprint.js v2 | data-fingerprint-apikey="fp_key" | | data-cookieless | ❌ | Enable cookieless tracking | data-cookieless="true" | | data-cross-domain | ❌ | Enable cross-domain tracking | data-cross-domain="true" | | data-auto-identify | ❌ | Auto-identify from forms | data-auto-identify="true" | | data-privacy-mode | ❌ | Hash emails with SHA1 | data-privacy-mode="true" | | data-interval-start | ❌ | Initial send interval (ms) | data-interval-start="5000" | | data-interval-increment | ❌ | Interval increment (ms) | data-interval-increment="2000" | | data-only-identify | ❌ | Only track identification | data-only-identify="true" |

📡 API Reference

Harmony.identify(identity, properties)

Identifies the current visitor.

// Simple email identification
Harmony.identify('[email protected]');

// With additional properties
Harmony.identify('[email protected]', {
  name: 'John Doe',
  company: 'Acme Corp',
  plan: 'enterprise'
});

// With object (for integrations)
Harmony.identify({
  email: '[email protected]',
  intercom_visitor_id: 'abc123'
});

Harmony.goal(name, properties, date)

Tracks a custom goal/event.

// Simple goal
Harmony.goal('Clicked Pricing');

// With properties
Harmony.goal('Viewed Demo', {
  demo_type: 'product_tour',
  duration: 120
});

// With custom date
Harmony.goal('Scheduled Meeting', {
  meeting_type: 'sales'
}, new Date('2025-09-10'));

Harmony.addSharedProperty(config)

Adds properties shared across all events (typically company data).

Harmony.addSharedProperty({
  key: 'company_id',
  value: 'acme_corp_123',
  properties: {
    company_name: 'Acme Corporation',
    company_domain: 'acme.com',
    company_industry: 'Technology',
    company_size: 500
  }
});

🔍 Event Types Tracked

Automatic Events

  • enter-page: Page views with referrer, URL, title
  • onclick: Click interactions with element details
  • onsubmit: Form submissions
  • onsearch: Search queries (on input[type="search"])
  • scroll-depth: Scroll percentage (25%, 50%, 75%, 100%)
  • identify: User identification events
  • onerror: JavaScript errors (if enabled)

Custom Events

Use Harmony.goal() to track any custom event relevant to your business.

Event Structure

Each event contains:

{
  actionLog: [{
    action: {
      actionType: "enter-page|onclick|onsubmit|scroll-depth|custom|identify",
      actionDate: "2025-09-06T10:30:00.000Z",
      actionElement: "button.cta-button", // for clicks
      actionText: "Get Started",          // for clicks
      actionUrl: "https://...",           // for clicks with links
      actionNumber: 75,                   // for scroll depth
      actionName: "Demo Completed",       // for custom goals
      actionProperties: {...}              // for custom goals
    },
    url: "https://yoursite.com/page"
  }],
  customerObject: {
    website: "yoursite.com",
    apiKey: "YOUR_API_KEY",
    version: "1.3.168",
    // ... other metadata
  },
  userObject: {
    uuid: "unique-visitor-id",
    identity: "[email protected]", // if identified
    // ... device/browser info
  }
}

📈 Event Volume Estimates

Typical Event Generation per Visitor Session

| Event Type | Average per Session | Notes | |------------|-------------------|--------| | Page Views | 3-5 events | One per page visited | | Clicks | 5-15 events | Depends on engagement | | Scroll Depth | 4-12 events | 4 per page (25%, 50%, 75%, 100%) | | Form Submissions | 0-2 events | Conversion dependent | | Custom Goals | 0-5 events | Business specific | | Total per Session | 12-39 events | Average ~20-25 events |

Daily Event Volume Projections

For capacity planning, here are typical daily event volumes:

| Site Size | Daily Visitors | Sessions/Day | Events/Day | Storage/Day | |-----------|---------------|--------------|------------|-------------| | Small Site | 100 | 150 | 3,000-6,000 | ~2-4 MB | | Medium Site | 1,000 | 1,500 | 30,000-60,000 | ~20-40 MB | | Large Site | 10,000 | 15,000 | 300,000-600,000 | ~200-400 MB | | Enterprise | 100,000 | 150,000 | 3M-6M | ~2-4 GB | | High Traffic | 1,000,000 | 1,500,000 | 30M-60M | ~20-40 GB |

Event Batching & Transmission

Events are batched to optimize network usage:

  • Initial batch: Sent after 5 seconds (configurable)
  • Subsequent batches: Every 2 seconds increment
  • Session end: All remaining events sent on page hide/unload
  • Batch size: Typically 5-20 events per request
  • Request size: ~1-3 KB per batch

Factors Affecting Event Volume

Higher volumes expected with:

  • Single Page Applications (more interactions, fewer page loads)
  • E-commerce sites (product browsing, cart interactions)
  • Content-heavy sites (more scroll events)
  • Sites with chat widgets (additional integration events)
  • B2B sites with multiple forms

Lower volumes expected with:

  • Simple landing pages
  • Blog/content sites with low interaction
  • Sites with high bounce rates
  • Mobile traffic (typically fewer interactions)

Storage Considerations

Per event storage:

  • Raw event: ~300-500 bytes
  • With enrichment: ~500-1000 bytes
  • Compressed: ~150-300 bytes

Monthly storage estimates:

  • Small site (3K events/day): ~100 MB/month
  • Medium site (50K events/day): ~1.5 GB/month
  • Large site (500K events/day): ~15 GB/month
  • Enterprise (5M events/day): ~150 GB/month

🧪 Testing & Debugging

Debug Mode

Add hsdebug=true to URL to enable verbose console logging:

https://yoursite.com?hsdebug=true

Staging Mode

Add hsstaging=true to test staging version:

https://yoursite.com?hsstaging=true

Test Server

A test server is included for local development:

cd test-server
npm install
npm start

Visit http://localhost:3000/dashboard to see tracked events.

🏗️ Development

Setup

npm install

Commands

# Run ESLint
npm run lint

# Auto-fix linting issues
npm run lint:fix

# Start test server
cd test-server && npm start

Building for Production

The library is distributed via NPM and served through JSDelivr CDN:

  1. Work in develop branch
  2. Bump version: npm version patch
  3. Publish to NPM: npm publish
  4. Push changes to git
  5. Purge CDN cache at https://www.jsdelivr.com/tools/purge

🔐 Privacy & Security

Data Collection

  • No personal information collected by default
  • IP addresses used for enrichment but not stored
  • Fingerprinting available but optional
  • GDPR/CCPA compliant options available

Privacy Mode

Enable with data-privacy-mode="true":

  • All emails are hashed with SHA1
  • No PII stored or transmitted in clear text

Cookieless Tracking

Enable with data-cookieless="true":

  • Uses fingerprinting instead of cookies
  • Maintains privacy while tracking visitors

📈 Performance Considerations

  • Script size: ~9.11 KB (minified + gzipped)
  • Batching: Events batched and sent at intervals
  • Beacon API: Primary transport with XHR fallback
  • Bot detection: Automatically filters bot traffic
  • Iframe handling: Configurable iframe tracking
  • Dynamic loading: Integrations loaded only when needed

🏛️ Backend Architecture

Current Implementation (MVP)

The current Harmony implementation uses a streamlined architecture optimized for simplicity and cost-effectiveness:

┌──────────────────────────────────────────────────────────────┐
│                     CLIENT LAYER                              │
│  Browser → Harmony.js → Batched Events → Beacon/XHR          │
└────────────────────────┬─────────────────────────────────────┘
                         │
┌────────────────────────▼─────────────────────────────────────┐
│                 PORTER INGESTION SERVICE                      │
│                                                               │
│  API Endpoints:                                              │
│    ├── /send → Direct to GCS                                 │
│    ├── /send-enrich → IP Enrichment + GCS                    │
│    └── /send-error → Error logging                           │
└────────────────────────┬─────────────────────────────────────┘
                         │
┌────────────────────────▼─────────────────────────────────────┐
│                   GOOGLE CLOUD STORAGE                        │
│                                                               │
│  Event Files (Partitioned by date/hour):                     │
│    └── /events/2025/09/06/14/{uuid}.json                     │
└────────────────────────┬─────────────────────────────────────┘
                         │
┌────────────────────────▼─────────────────────────────────────┐
│                    BATCH PROCESSING                           │
│                                                               │
│  BigQuery Scheduled Queries:                                 │
│    ├── Hourly: Session aggregation                           │
│    ├── Daily: User journey analysis                          │
│    └── Weekly: Cohort analysis                               │
└───────────────────────────────────────────────────────────────┘

This architecture handles:

  • 100K-1M events/day comfortably
  • < 500ms ingestion latency
  • $10-50/month infrastructure cost
  • Simple debugging and maintenance

Future Architecture (Scale Phase)

When you need real-time processing, multiple consumers, or > 10M events/day:

┌──────────────────────────────────────────────────────────────┐
│                     CLIENT LAYER                              │
│  Browser → Harmony.js → Batched Events → Beacon/XHR          │
└────────────────────────┬─────────────────────────────────────┘
                         │
┌────────────────────────▼─────────────────────────────────────┐
│                    INGESTION LAYER                            │
│                                                               │
│  Load Balancer → API Gateway                                 │
│    ├── /send → Pub/Sub Topic                                 │
│    ├── /send-fast → Priority Topic                           │
│    ├── /send-enrich → Enrichment Service                     │
│    └── /send-error → Error Topic                             │
└────────────────────────┬─────────────────────────────────────┘
                         │
┌────────────────────────▼─────────────────────────────────────┐
│                  GOOGLE PUB/SUB                               │
│                                                               │
│  Topics & Subscriptions:                                     │
│    ├── events-main → Multiple subscribers                    │
│    ├── events-priority → Fast processing                     │
│    └── events-dlq → Failed message handling                  │
└────────────────────────┬─────────────────────────────────────┘
                         │
        ┌────────────────┴────────────────┐
        ▼                                 ▼
┌──────────────────┐            ┌──────────────────┐
│  STREAM PROCESS  │            │   GCS WRITER     │
│                  │            │                  │
│  • Real-time     │            │  • Batch files   │
│  • Enrichment    │            │  • Compression   │
│  • Alerts        │            │  • Partitioning  │
└──────────────────┘            └──────────────────┘
        │                                 │
        ▼                                 ▼
┌──────────────────┐            ┌──────────────────┐
│   REAL-TIME DB   │            │   COLD STORAGE   │
│   (Firestore)    │            │      (GCS)       │
└──────────────────┘            └──────────────────┘

When to Add Pub/Sub

You should consider adding Pub/Sub when:

  • Traffic exceeds 1M events/day consistently
  • You need real-time dashboards (< 1 second latency)
  • Multiple systems need to consume the same events
  • You're experiencing data loss due to processing delays
  • You need guaranteed delivery with retry logic
  • Different event types need different processing speeds

Signs you DON'T need Pub/Sub yet:

  • Batch processing (hourly/daily) is sufficient
  • Single consumer (just storing to GCS/BigQuery)
  • < 100K events/day
  • Cost optimization is priority
  • Team is small (less complexity to manage)

Migration Path from Direct-to-GCS to Pub/Sub

The migration is straightforward when needed:

// Current Implementation (Direct to GCS)
async function handleEvent(event) {
  const file = storage.bucket('events')
    .file(`${date}/${uuid}.json`);
  await file.save(JSON.stringify(event));
}

// Future Implementation (With Pub/Sub)
async function handleEvent(event) {
  // Just publish to topic - minimal code change
  await pubsub.topic('events').publish({
    data: Buffer.from(JSON.stringify(event))
  });
}

// Subscriber handles GCS writing (separate service)
subscription.on('message', async (message) => {
  const event = JSON.parse(message.data.toString());
  await writeToGCS(event);
  message.ack();
});

Migration steps:

  1. Deploy Pub/Sub subscriber that writes to GCS
  2. Update ingestion service to write to both GCS and Pub/Sub
  3. Verify data consistency
  4. Switch to Pub/Sub only
  5. Add more subscribers as needed

Architecture Benefits by Stage

| Stage | Architecture | Benefits | Limitations | |-------|-------------|----------|-------------| | MVP | Direct to GCS | Simple, cheap, reliable | No real-time, single consumer | | Growth | GCS + Cache | Fast recent data access | Still batch-oriented | | Scale | Pub/Sub + GCS | Real-time, multiple consumers | More complex, higher cost | | Enterprise | Full streaming | Sub-second latency, complex processing | Requires dedicated team |

Cost Comparison

Direct to GCS (Current):

  • GCS Storage: ~$0.02/GB/month
  • GCS Operations: ~$0.005/10K operations
  • Total: $10-50/month for 1M events/day

With Pub/Sub (Future):

  • Pub/Sub: ~$40/TB
  • Additional GCS costs (same as above)
  • Cloud Functions/Run for processing
  • Total: $100-500/month for 1M events/day

The current architecture is perfectly adequate for most B2B SaaS companies. HockeyStack likely started with direct storage too and evolved as they scaled.

📝 License

This project is proprietary software. All rights reserved.

🆘 Support

For issues, questions, or feature requests, please contact the Harmony support team.