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

sparrow-starter-kit

v1.0.0

Published

comprehensive OAuth integration framework for automated survey distribution with data mapping and trigger capabilities

Readme

🚀 Integration Framework

A comprehensive OAuth integration framework for automated survey distribution with data mapping and trigger capabilities

License: MIT Node.js Version

✨ Features

  • 🔐 OAuth 2.0 Integration - Complete authentication flow with automatic token refresh
  • 🗺️ Data Mapping Engine - Transform external data to survey variables
  • ⚡ Trigger System - Event-driven survey automation with polling capabilities
  • 💾 Persistent Storage - Built-in storage management for tokens and configurations
  • 📊 Multi-Channel Support - Email, SMS, and other survey distribution channels
  • 🔄 FHIR API Integration - Healthcare data polling and processing
  • 📈 Comprehensive Logging - Detailed monitoring and error tracking

📦 Installation

npm install blindgate

🚀 Quick Start

Import All Components

const { BackendTemplate, Mapping, Trigger } = require('blindgate');

🔐 OAuth Integration (BackendTemplate)

Handle complete OAuth 2.0 flow with automatic token management:

const integration = new BackendTemplate(
  'appnest-user-123',           // AppNest User ID
  'product-456',                // Product ID  
  'user-789',                   // User ID
  'your-client-id',             // OAuth Client ID
  'your-client-secret',         // OAuth Client Secret
  'https://your-app.com/callback', // Redirect URI
  'MyIntegration'               // Integration Name
);

// Exchange authorization code for tokens
async function authenticate(authCode) {
  try {
    const tokens = await integration.exchangeAuthCode(authCode);
    console.log('Authentication successful:', tokens);
  } catch (error) {
    console.error('Auth failed:', error);
  }
}

// Make authenticated API requests
async function fetchData() {
  try {
    const response = await integration.makeRequest({
      method: 'GET',
      endPoint: '/v3/users',
      queryString: 'limit=10'
    });
    return response.data;
  } catch (error) {
    console.error('API request failed:', error);
  }
}

// Check user features
async function checkFeature() {
  const hasAdvancedFeature = await integration.checkHasFeature('advanced_surveys');
  console.log('Advanced surveys available:', hasAdvancedFeature);
}

🗺️ Data Mapping Engine

Transform external system data to survey-compatible format:

const mapper = new Mapping(parentLayer, 'MyIntegration');

// Save field mappings
async function setupMappings() {
  const mappings = [
    {
      fields: [
        {
          surveySparrowField: { value: 'patient_name' },
          integrationField: { value: 'fullName' },
          mappedType: { value: 'QUESTION' },
          defaultValue: { value: 'Unknown Patient' }
        },
        {
          surveySparrowField: { value: 'appointment_date' },
          integrationField: { value: 'scheduledDate' },
          mappedType: { value: 'VARIABLE' },
          defaultValue: { value: new Date().toISOString() }
        }
      ]
    }
  ];

  await mapper.handleSaveMapping('survey-123', mappings, parentLayer);
}

// Transform data using mappings
async function transformData() {
  const externalData = {
    data: {
      answers: { 'patient_name': { answer: 'John Doe' } },
      variables: { 'appointment_date': '2024-01-15T10:00:00Z' },
      expressions: {},
      contact: { email: '[email protected]', phone: '+1234567890' }
    }
  };

  const mapping = await mapper.handleGetMappings('survey-123', parentLayer);
  const transformedData = await mapper.getAnswerMapping(mapping.data.data[0], externalData);
  
  console.log('Transformed data:', transformedData);
  // Output: { fullName: 'John Doe', scheduledDate: '2024-01-15T10:00:00Z' }
}

⚡ Trigger System

Automate survey distribution based on external events:

const triggerEngine = new Trigger(parentLayer, 'MyIntegration');

// Setup polling triggers
async function setupTriggers() {
  const triggers = [
    {
      id: 'patient-survey-trigger',
      isEnabled: true,
      object: 'Patient', // FHIR resource type
      triggerDetails: {
        variables: [
          { value: 'patient_id', type: 'TEXT' },
          { value: 'visit_date', type: 'DATE' },
          { value: 'satisfaction_score', type: 'NUMBER' }
        ]
      }
    }
  ];

  // Save triggers and start polling (every 10 minutes)
  await triggerEngine.handleSaveTrigger(
    'survey-123',           // Survey ID
    triggers,               // Trigger configurations
    'patient-survey-trigger', // Trigger ID
    parentLayer,            // Context
    true                    // Enable polling
  );
}

// Handle scheduled polling events
async function handlePollingEvent() {
  await triggerEngine.handleScheduleEvent('survey-123', parentLayer);
}

// Manual survey triggering
async function triggerSurvey() {
  const variables = {
    patient_id: 'P-12345',
    visit_date: '2024-01-15',
    satisfaction_score: 0
  };

  const contact = {
    email: '[email protected]',
    firstName: 'John',
    lastName: 'Doe'
  };

  await triggerEngine.triggerSurvey(
    'survey-123',
    variables,
    contact,
    'EMAIL' // or 'SMS'
  );
}

🏗️ Complete Integration Example

Build a healthcare patient feedback system:

const { BackendTemplate, Mapping, Trigger } = require('blindgate');

class HealthcareIntegration {
  constructor(config) {
    this.oauth = new BackendTemplate(
      config.appnestUserId,
      config.productId,
      config.userId,
      config.clientId,
      config.clientSecret,
      config.redirectUri,
      'HealthcareFeedback'
    );
    
    this.mapper = new Mapping(config.parentLayer, 'HealthcareFeedback');
    this.trigger = new Trigger(config.parentLayer, 'HealthcareFeedback');
    this.surveyId = config.surveyId;
    this.parentLayer = config.parentLayer;
  }

  async initialize(authCode) {
    // 1. Complete OAuth flow
    await this.oauth.exchangeAuthCode(authCode);
    
    // 2. Setup field mappings
    await this.setupFieldMappings();
    
    // 3. Configure triggers
    await this.setupEventTriggers();
    
    console.log('Healthcare integration initialized successfully');
  }

  async setupFieldMappings() {
    const mappings = [{
      fields: [
        {
          surveySparrowField: { value: 'patient_name' },
          integrationField: { value: 'fullName' },
          mappedType: { value: 'CONTACT' }
        },
        {
          surveySparrowField: { value: 'appointment_type' },
          integrationField: { value: 'serviceType' },
          mappedType: { value: 'VARIABLE' }
        },
        {
          surveySparrowField: { value: 'provider_name' },
          integrationField: { value: 'doctorName' },
          mappedType: { value: 'VARIABLE' }
        }
      ]
    }];

    await this.mapper.handleSaveMapping(this.surveyId, mappings, this.parentLayer);
  }

  async setupEventTriggers() {
    const triggers = [{
      id: 'appointment-completed',
      isEnabled: true,
      object: 'Encounter',
      triggerDetails: {
        variables: [
          { value: 'appointment_id', type: 'TEXT' },
          { value: 'completion_time', type: 'DATETIME' },
          { value: 'service_type', type: 'TEXT' }
        ]
      }
    }];

    await this.trigger.handleSaveTrigger(
      this.surveyId,
      triggers,
      'appointment-completed',
      this.parentLayer,
      true // Enable polling
    );
  }

  async sendPatientSurvey(patientData, appointmentData) {
    const variables = {
      appointment_id: appointmentData.id,
      service_type: appointmentData.serviceType,
      doctor_name: appointmentData.provider
    };

    const contact = {
      email: patientData.email,
      firstName: patientData.firstName,
      lastName: patientData.lastName,
      phone: patientData.phone
    };

    await this.trigger.triggerSurvey(
      this.surveyId,
      variables,
      contact,
      'EMAIL'
    );
  }
}

// Usage
const integration = new HealthcareIntegration({
  appnestUserId: 'user-123',
  productId: 'healthcare-app',
  userId: 'clinic-456',
  clientId: process.env.OAUTH_CLIENT_ID,
  clientSecret: process.env.OAUTH_CLIENT_SECRET,
  redirectUri: 'https://myapp.com/oauth/callback',
  surveyId: 'patient-satisfaction-survey',
  parentLayer: { /* context object */ }
});

🔧 Storage Management

Both BackendTemplate and components provide storage utilities:

// Store configuration data
await integration.setStorageV2({
  key: 'integration_settings',
  data: { 
    pollInterval: 600000,
    maxRetries: 3,
    enableNotifications: true
  }
});

// Retrieve stored data
const settings = await integration.getStorageV2({ 
  key: 'integration_settings' 
});

📊 Error Handling & Logging

The framework includes comprehensive error handling:

try {
  await integration.makeRequest({
    method: 'POST',
    endPoint: '/v3/surveys/trigger',
    payload: surveyData
  });
} catch (error) {
  // Automatic error logging and alerting
  await integration.alert(error);
  console.error('Survey trigger failed:', error);
}

🛠️ API Reference

BackendTemplate

  • makeRequest(options) - Authenticated HTTP requests
  • exchangeAuthCode(code) - OAuth token exchange
  • getUser() - Get current user info
  • checkHasFeature(feature) - Feature availability check
  • setStorageV2(data) / getStorageV2(key) - Data persistence

Mapping

  • handleSaveMapping(id, mappings, context) - Save field mappings
  • handleGetMappings(id, context) - Retrieve mappings
  • getAnswerMapping(mapping, data) - Transform data

Trigger

  • handleSaveTrigger(surveyId, triggers, triggerId, context, polling) - Configure triggers
  • triggerSurvey(surveyId, variables, contact, channel) - Send survey
  • handleScheduleEvent(surveyId, context) - Process scheduled events

📄 License

MIT © 2024