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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@encatch/web-sdk

v0.0.28

Published

A powerful, lightweight JavaScript SDK for collecting user feedback and running surveys on web applications

Readme

Encatch Web SDK

A powerful, lightweight JavaScript SDK for collecting user feedback and running surveys on web applications. Built with Preact and TypeScript, the Encatch Web SDK provides intelligent trigger-based feedback collection with advanced targeting and frequency control.

Features

  • Intelligent Triggers: Automatically display feedback based on user behavior
    • Page load triggers
    • Custom event triggers
    • Scroll-based triggers
    • Time-delayed triggers
  • User Session Management: Track and manage user sessions with device fingerprinting
  • Frequency Control: Configure feedback display frequency and scheduling
  • Multi-language Support: Display feedback in multiple languages
  • Theme Support: Light and dark mode with custom CSS
  • Prepopulated Answers: Pre-fill feedback forms with known data
  • Event Tracking: Track user interactions and custom events
  • TypeScript Support: Fully typed API for better developer experience
  • Lightweight: Minimal bundle size with optimized performance

Installation

Via Script Tag (Recommended)

Add the following script to your HTML file before the closing </head> tag:

<script>
  !function(){if(!window.encatch){var e={_i:[],apiKey:"",config:{host:"https://your-host.com",autoStartEnabled:!1,autoStartSessionDisabled:!0,themeMode:"light",language:"en",customCssLink:""},initialized:!1,chunkUrlLoader:function(e){return window.encatch.config.host+e},init:function(n,t){if(!("initialized"in window.encatch&&window.encatch.initialized)){if(window.encatch.apiKey=n,t?.host&&(window.encatch.config.host=t.host),t?.identity&&(window.encatch.config.identity=t.identity),void 0!==t?.autoStartSessionDisabled&&(window.encatch.config.autoStartSessionDisabled=t.autoStartSessionDisabled),void 0!==t?.processAfterIdentitySet?window.encatch.config.processAfterIdentitySet=t.processAfterIdentitySet:window.encatch.config.processAfterIdentitySet=!1,void 0!==t?.autoStartEnabled&&(window.encatch.config.autoStartEnabled=t.autoStartEnabled),t?.themeMode?window.encatch.config.themeMode=t.themeMode:window.encatch.config.themeMode="light",t?.language?window.encatch.config.language=t.language:window.encatch.config.language="en",t?.customCssLink&&(window.encatch.config.customCssLink=t.customCssLink),t?.setUser&&(window.encatch.config.setUser=t.setUser),window.encatch.initialized=!0,!import.meta.env.VITE_PATH_PREFIX||import.meta.env.VITE_PATH_PREFIX.startsWith("/")||(import.meta.env.VITE_PATH_PREFIX="/"+import.meta.env.VITE_PATH_PREFIX),import.meta.env.VITE_PATH_PREFIX=import.meta.env.VITE_PATH_PREFIX.replace(/\/+$/,""),scriptSrc=`${window.encatch.config.host}${import.meta.env.VITE_PATH_PREFIX||""}/encatch-web-sdk.js`,(script=document.createElement("script")).src=scriptSrc,script.type="module",script.async=!0,firstHeadElement=document.head.firstChild)firstHeadElement?document.head.insertBefore(script,firstHeadElement):document.head.appendChild(script);var o=document.createElement("div");o.id="enisght-root",document.body.appendChild(o)}}};window.encatch=e,["trackEvent","stop","start","setUser","setThemeMode","setLanguage","openFeedbackById","openFeedbackByName","verifyFeedbackIds","forceFetchEligibleFeedbacks","capturePageScrollEvent"].forEach((function(n){e[n]=function(){for(var t=arguments.length,o=new Array(t),i=0;i<t;i++)o[i]=arguments[i];window.encatch._i.push([n].concat(o))}}))}}();
</script>

Then initialize the SDK:

<script>
  encatch.init('YOUR_API_KEY', {
    host: 'https://your-host.com',
    autoStartEnabled: true
  });
</script>

Via NPM (For Module Bundlers)

npm install @encatch/web-sdk
import '@encatch/web-sdk';

encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true
});

Quick Start

Basic Setup

// Initialize the SDK
encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true,
  themeMode: 'light',
  language: 'en'
});

// Set user identity (optional)
encatch.setUser('user-123', {
  $set: {
    email: '[email protected]',
    name: 'John Doe',
    plan: 'premium'
  }
});

// Track custom events
encatch.trackEvent('button_clicked', {
  path: '/dashboard',
  button_id: 'upgrade-button'
});

Manual Trigger

// Open feedback by ID
encatch.openFeedbackById('feedback-config-id-123');

// Open feedback by name
encatch.openFeedbackByName('Customer Satisfaction Survey');

// With theme and language override
encatch.openFeedbackById('feedback-config-id-123', 'dark', 'es');

Configuration Options

Initialization Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | host | string | - | Required. The host URL for the Encatch API | | autoStartEnabled | boolean | false | Automatically start fetching eligible feedbacks | | autoStartSessionDisabled | boolean | true | Disable automatic session management | | processAfterIdentitySet | boolean | false | Wait for user identity before processing | | themeMode | 'light' \| 'dark' | 'light' | Default theme mode | | language | string | 'en' | Default language code | | customCssLink | string | - | URL to custom CSS file | | setUser | UserInfo | - | Initial user identity | | customProperties | Record<string, string> | - | Custom properties for targeting | | onSessionDisabled | (action, message) => void | - | Callback when session functions are disabled |

Example: Complete Configuration

encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true,
  autoStartSessionDisabled: false,
  themeMode: 'light',
  language: 'en',
  customCssLink: 'https://your-cdn.com/custom-theme.css',
  setUser: {
    userId: 'user-123',
    traits: {
      $set: {
        email: '[email protected]',
        plan: 'premium'
      }
    }
  },
  onSessionDisabled: (action, message) => {
    console.warn(`Session action blocked: ${action} - ${message}`);
  }
});

API Reference

init(apiKey, options)

Initialize the Encatch SDK.

encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true
});

start(userId?, traits?)

Start the SDK and optionally set user identity. Useful when autoStartEnabled: false.

// Start without user
encatch.start();

// Start with user identity
encatch.start('user-123', {
  $set: {
    email: '[email protected]',
    name: 'John Doe'
  }
});

stop()

Stop the SDK. Behavior depends on initialization:

  • If initialized with autoStartEnabled: false: Clears all data, feedbacks, and frequency tracking
  • If initialized with autoStartEnabled: true: Makes user anonymous but retains feedbacks
encatch.stop();

setUser(userId?, traits?)

Set or update user identity.

// Set user identity
encatch.setUser('user-123', {
  $set: { email: '[email protected]', plan: 'premium' },
  $set_once: { signup_date: '2024-01-01' },
  $counter: { logins: 1 },
  $unset: ['temp_property']
});

// Make user anonymous
encatch.setUser();

User Traits Operators

  • $set: Set property value (overwrites existing)
  • $set_once: Set only if property doesn't exist
  • $counter: Increment counter by value
  • $unset: Remove properties (array of property names)

trackEvent(eventName, properties?)

Track custom events for trigger matching.

encatch.trackEvent('purchase_completed', {
  path: '/checkout/success',
  product_id: 'prod-123',
  amount: 99.99
});

openFeedbackById(feedbackConfigId, theme?, language?, event?, customProperties?, prepopulatedAnswers?)

Manually open a feedback form by its configuration ID.

// Basic usage
encatch.openFeedbackById('feedback-config-id-123');

// With theme and language override
encatch.openFeedbackById('feedback-config-id-123', 'dark', 'es');

// With prepopulated answers
encatch.openFeedbackById('feedback-config-id-123', 'light', 'en', undefined, undefined, [
  {
    sectionIndex: 0,
    questionIndex: 0,
    value: { text: 'Pre-filled answer' }
  }
]);

openFeedbackByName(feedbackName, theme?, language?, event?, customProperties?, prepopulatedAnswers?)

Manually open a feedback form by its name.

encatch.openFeedbackByName('Customer Satisfaction Survey');

// With options
encatch.openFeedbackByName('NPS Survey', 'dark', 'es');

setThemeMode(theme)

Change the theme mode dynamically.

encatch.setThemeMode('dark');
encatch.setThemeMode('light');

setLanguage(language)

Change the language dynamically.

encatch.setLanguage('es'); // Spanish
encatch.setLanguage('fr'); // French
encatch.setLanguage('en'); // English

forceFetchEligibleFeedbacks()

Force refresh the list of eligible feedbacks.

await encatch.forceFetchEligibleFeedbacks();

verifyFeedbackIds(feedbackIds)

Verify which feedback IDs are currently eligible.

const validIds = encatch.verifyFeedbackIds([
  'feedback-id-1',
  'feedback-id-2',
  'feedback-id-3'
]);
console.log('Valid feedback IDs:', validIds);

capturePageScrollEvent(scrollPercent)

Manually trigger scroll-based feedback.

// Track scroll percentage
window.addEventListener('scroll', () => {
  const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
  encatch.capturePageScrollEvent(`${scrollPercent}%`);
});

Event Tracking

Automatic Page Tracking

The SDK automatically tracks page changes in single-page applications:

// Page changes are tracked automatically
// Triggers "pageLoad" event for configured triggers

Custom Events

Track custom events for business logic:

// E-commerce example
encatch.trackEvent('product_viewed', {
  path: '/products/123',
  category: 'electronics',
  price: 299.99
});

encatch.trackEvent('add_to_cart', {
  path: '/products/123',
  product_id: 'prod-123'
});

encatch.trackEvent('checkout_started', {
  path: '/checkout',
  cart_value: 599.98
});

Scroll Tracking

// Automatic scroll tracking (if configured in feedback triggers)
// Or manual tracking:
let scrollTriggered = false;

window.addEventListener('scroll', () => {
  const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;

  if (scrollPercent > 50 && !scrollTriggered) {
    encatch.capturePageScrollEvent('50%');
    scrollTriggered = true;
  }
});

Advanced Usage

Prepopulated Answers

Pre-fill feedback forms with known data:

encatch.openFeedbackById('feedback-config-id', 'light', 'en', undefined, undefined, [
  {
    sectionIndex: 0,  // First section
    questionIndex: 0, // First question
    value: { text: 'Pre-filled text answer' }
  },
  {
    sectionIndex: 0,
    questionIndex: 1,
    value: { rating: 5 }
  }
]);

Custom Properties for Targeting

Pass custom properties for advanced targeting:

encatch.openFeedbackById(
  'feedback-config-id',
  'light',
  'en',
  undefined,
  {
    user_segment: 'premium',
    feature_flag: 'beta_features',
    experiment_group: 'variant_a'
  }
);

Session Management

// Disable automatic session management
encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartSessionDisabled: false
});

// Manual session control (when autoStartSessionDisabled: false)
encatch.start(); // Start a new session
encatch.stop();  // End current session

Dynamic Theme Switching

// Listen to system theme changes
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');

darkModeQuery.addEventListener('change', (e) => {
  encatch.setThemeMode(e.matches ? 'dark' : 'light');
});

// Or use a theme toggle
function toggleTheme() {
  const currentTheme = getCurrentTheme(); // Your theme logic
  encatch.setThemeMode(currentTheme === 'dark' ? 'light' : 'dark');
}

Custom CSS Styling

// Option 1: Provide custom CSS URL during initialization
encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  customCssLink: 'https://your-cdn.com/custom-encatch-theme.css'
});

// Option 2: Configure custom CSS per feedback in the Encatch dashboard

Conditional Feedback Display

// Only show feedback to premium users
if (user.plan === 'premium') {
  encatch.openFeedbackById('premium-user-feedback');
}

// Show different feedback based on user journey
if (isNewUser) {
  encatch.openFeedbackByName('Onboarding Feedback');
} else {
  encatch.openFeedbackByName('Feature Usage Survey');
}

Form Event Listeners

The SDK provides two approaches for listening to feedback form events: config-based and runtime-based.

Available Form Events

| Event | Payload | Description | |-------|---------|-------------| | form:submit | { feedbackIdentifier: string, feedbackConfigurationId: string, completionTimeInSeconds: number, response: Object } | Emitted when the user completes and submits the feedback form | | form:view | { feedbackIdentifier: string, feedbackConfigurationId: string } | Emitted when the feedback form is displayed to the user | | form:close | { feedbackIdentifier: string, feedbackConfigurationId: string } | Emitted when the user closes the feedback form (with or without submitting) | | form:questionAnswered | { questionId: string, answer: Object } | Emitted when the user answers a question | | form:sectionChange | { sectionIndex: number } | Emitted when the user navigates to a different section | | form:error | { error: string } | Emitted when an error occurs during form interaction |

Approach 1: Config-Based (Recommended)

Define event handlers during SDK initialization using the onFormEvent callback:

encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true,
  onFormEvent: (formEvent) => {
    // Listen to form submission
    formEvent.onSubmit((data) => {
      console.log('Form submitted:', data);
      console.log('Feedback ID:', data.feedbackIdentifier);
      console.log('Config ID:', data.feedbackConfigurationId);
      console.log('Completion time:', data.completionTimeInSeconds);

      // Send to analytics
      analytics.track('Feedback Submitted', {
        feedbackId: data.feedbackIdentifier,
        configId: data.feedbackConfigurationId
      });
    });

    // Listen to form view
    formEvent.onView((data) => {
      console.log('Form viewed:', data);
    });

    // Listen to form close
    formEvent.onClose((data) => {
      console.log('Form closed:', data);
    });

    // Listen to question answered
    formEvent.onQuestionAnswered((data) => {
      console.log('Question answered:', data.questionId, data.answer);
    });

    // Listen to section change
    formEvent.onSectionChange((data) => {
      console.log('Section changed to:', data.sectionIndex);
    });

    // Listen to errors
    formEvent.onError((data) => {
      console.error('Form error:', data.error);
    });
  }
});

Approach 2: Runtime-Based

Subscribe to events at runtime using the encatch.on() method:

// Subscribe to form submission
const unsubscribeSubmit = encatch.on('form:submit', (data) => {
  console.log('Form submitted:', data);
  alert(`Feedback submitted! ID: ${data.feedbackIdentifier}`);
});

// Subscribe to form view
const unsubscribeView = encatch.on('form:view', (data) => {
  console.log('Form viewed:', data);
});

// Subscribe to form close
const unsubscribeClose = encatch.on('form:close', (data) => {
  console.log('Form closed:', data);
});

// Subscribe to question answered
const unsubscribeQuestion = encatch.on('form:questionAnswered', (data) => {
  console.log('Question answered:', data);
});

// Subscribe to section change
const unsubscribeSection = encatch.on('form:sectionChange', (data) => {
  console.log('Section changed:', data);
});

// Subscribe to errors
const unsubscribeError = encatch.on('form:error', (data) => {
  console.error('Form error:', data);
});

// Unsubscribe when no longer needed
unsubscribeSubmit();
unsubscribeView();

Integration Examples

Google Analytics Integration

encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true,
  onFormEvent: (formEvent) => {
    formEvent.onView((data) => {
      gtag('event', 'feedback_viewed', {
        feedback_id: data.feedbackIdentifier,
        config_id: data.feedbackConfigurationId
      });
    });

    formEvent.onSubmit((data) => {
      gtag('event', 'feedback_submitted', {
        feedback_id: data.feedbackIdentifier,
        config_id: data.feedbackConfigurationId,
        completion_time: data.completionTimeInSeconds
      });
    });

    formEvent.onClose((data) => {
      gtag('event', 'feedback_closed', {
        feedback_id: data.feedbackIdentifier
      });
    });
  }
});

Segment Analytics Integration

encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true,
  onFormEvent: (formEvent) => {
    formEvent.onSubmit((data) => {
      analytics.track('Feedback Completed', {
        feedbackId: data.feedbackIdentifier,
        configId: data.feedbackConfigurationId,
        duration: data.completionTimeInSeconds
      });
    });

    formEvent.onQuestionAnswered((data) => {
      analytics.track('Question Answered', {
        questionId: data.questionId,
        answerType: data.answer.answer_type
      });
    });
  }
});

Custom Data Layer (GTM)

encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true,
  onFormEvent: (formEvent) => {
    formEvent.onSubmit((data) => {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: 'feedback_submitted',
        feedbackId: data.feedbackIdentifier,
        configId: data.feedbackConfigurationId,
        completionTime: data.completionTimeInSeconds
      });
    });
  }
});

CRM Integration (e.g., Salesforce, HubSpot)

encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true,
  onFormEvent: (formEvent) => {
    formEvent.onSubmit(async (data) => {
      // Send to your CRM
      await fetch('https://your-crm.com/api/feedback', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          feedbackId: data.feedbackIdentifier,
          userId: window.currentUserId,
          response: data.response,
          timestamp: new Date().toISOString()
        })
      });
    });
  }
});

TypeScript Support

The SDK is fully typed. TypeScript definitions are included:

import '@encatch/web-sdk';

// All methods are typed
encatch.init('YOUR_API_KEY', {
  host: 'https://your-host.com',
  autoStartEnabled: true,
  themeMode: 'light', // Type: 'light' | 'dark'
  language: 'en'
});

// User traits are typed
encatch.setUser('user-123', {
  $set: { email: '[email protected]' },
  $set_once: { signup_date: '2024-01-01' },
  $counter: { logins: 1 },
  $unset: ['temp_field']
});

// Event properties are typed
encatch.trackEvent('custom_event', {
  path: '/page',
  event: 'click'
});

Development

Prerequisites

  • Node.js 18+
  • pnpm

Setup

# Install dependencies
pnpm install

# Start development server
pnpm dev

# Build for production
pnpm build

# Preview production build
pnpm preview

Project Structure

packages/web-sdk/
├── src/
│   ├── @types/          # TypeScript type definitions
│   ├── hooks/           # Preact hooks for functionality
│   ├── utils/           # Utility functions
│   ├── index.tsx        # Main SDK initialization
│   ├── core-wrapper.tsx # Core SDK logic
│   └── surveySDK.ts     # Event SDK
├── dist-sdk/            # Build output
├── package.json
├── vite.config.ts       # Vite configuration
└── README.md

Build Output

The build produces:

  • encatch-web-sdk.js - Main SDK bundle
  • encatch-web-sdk.css - Default styles
  • preview-sdk.js - Preview/demo version
  • embedded-sdk.js - Embedded version

Browser Support

  • Chrome 62+
  • Firefox 59+
  • Safari 12+
  • iOS Safari 10.3+
  • Opera 50+
  • Edge (Chromium-based)

License

Private - Copyright (c) Encatch

Support

For issues, questions, or feature requests, please contact your Encatch account manager or visit the Encatch Dashboard.