@bytelyst/survey-client (0.1.0)

Published 2026-03-29 07:53:29 +00:00 by bytelyst-admin

Installation

@bytelyst:registry=
npm install @bytelyst/survey-client@0.1.0
"@bytelyst/survey-client": "0.1.0"

About this package

@bytelyst/survey-client

TypeScript client for the ByteLyst Survey platform. Provides survey discovery, question flow management, response submission, and offline caching.

Installation

npm install @bytelyst/survey-client
# or
pnpm add @bytelyst/survey-client

Quick Start

import { createSurveyClient } from '@bytelyst/survey-client';

const client = createSurveyClient({
  baseURL: 'https://api.bytelyst.io/v1',
  productId: 'lysnrai',
  getAuthToken: async () => {
    return localStorage.getItem('token');
  }
});

// Check for active survey
const [survey, error] = await client.getActiveSurvey();
if (survey) {
  console.log('Survey available:', survey.title);
}

API Reference

createSurveyClient(config)

Creates a new survey client instance.

Config:

Option Type Required Description
baseURL string Yes API base URL
productId string Yes Product identifier
getAuthToken () => Promise Yes Function to retrieve JWT token
enableOfflineCache boolean No Enable offline response caching (default: true)

Methods

getActiveSurvey()

Check if there's an active survey for the current user.

const [survey, error] = await client.getActiveSurvey();
// Returns: ActiveSurvey | null

startSurvey(surveyId: string)

Start a survey session.

const [state, error] = await client.startSurvey('srv_123');
// Returns: { surveyStateId: string, currentQuestionIndex: number }

submitAnswer(surveyId: string, questionId: string, answer: SurveyAnswer)

Submit an answer for a specific question.

const [result, error] = await client.submitAnswer('srv_123', 'q1', {
  type: 'rating',
  value: { value: 8 }
});
// Returns: { currentQuestionIndex: number, nextQuestionId: string, isComplete: boolean }

Answer Types:

// Single choice
{ type: 'single_choice', value: { value: 'option_1' } }

// Multiple choice
{ type: 'multiple_choice', value: { values: ['opt_1', 'opt_2'] } }

// Rating/NPS/Scale
{ type: 'rating', value: { value: 8 } }

// Text
{ type: 'text', value: { value: 'My feedback here' } }

// Ranking
{ type: 'ranking', value: { order: ['opt_1', 'opt_2', 'opt_3'] } }

completeSurvey(surveyId: string)

Mark survey as complete and claim any incentive.

const [completion, error] = await client.completeSurvey('srv_123');
// Returns: { success: boolean, incentiveClaimed: boolean, incentiveType?: string, incentiveAmount?: number }

dismissSurvey(surveyId: string)

Dismiss survey without completing.

await client.dismissSurvey('srv_123');

startPolling(intervalMs: number, callback: (survey) => void)

Start polling for active surveys.

client.startPolling(60000, (survey) => {
  if (survey) {
    showSurveyModal(survey);
  }
});

stopPolling()

Stop survey polling.

client.stopPolling();

flushOfflineQueue()

Manually flush any cached offline responses.

await client.flushOfflineQueue();

React Integration

Hook Usage

import { useSurvey } from './hooks/useSurvey';

function SurveyWidget() {
  const { 
    activeSurvey, 
    currentQuestion, 
    submitAnswer, 
    completeSurvey,
    progress 
  } = useSurvey({
    autoStart: true,
    pollingInterval: 60000
  });

  if (!activeSurvey) return null;

  return (
    <SurveyModal
      survey={activeSurvey}
      currentQuestion={currentQuestion}
      progress={progress}
      onSubmit={submitAnswer}
      onComplete={completeSurvey}
    />
  );
}

Complete Survey Flow Example

function SurveyFlow() {
  const client = useSurveyClient();
  const [survey, setSurvey] = useState<ActiveSurvey | null>(null);
  const [currentIndex, setCurrentIndex] = useState(0);
  const [answers, setAnswers] = useState<Record<string, SurveyAnswer>>({});

  useEffect(() => {
    loadSurvey();
  }, []);

  async function loadSurvey() {
    const [activeSurvey] = await client.getActiveSurvey();
    if (activeSurvey) {
      await client.startSurvey(activeSurvey.id);
      setSurvey(activeSurvey);
    }
  }

  async function handleAnswer(questionId: string, answer: SurveyAnswer) {
    const [result] = await client.submitAnswer(survey!.id, questionId, answer);
    
    setAnswers(prev => ({ ...prev, [questionId]: answer }));
    setCurrentIndex(result.currentQuestionIndex);

    if (result.isComplete) {
      const [completion] = await client.completeSurvey(survey!.id);
      if (completion.incentiveClaimed) {
        showIncentiveToast(completion.incentiveAmount, completion.incentiveType);
      }
    }
  }

  if (!survey) return null;

  const question = survey.questions[currentIndex];
  const isLast = currentIndex === survey.questions.length - 1;

  return (
    <QuestionView
      question={question}
      onSubmit={(answer) => handleAnswer(question.id, answer)}
      onSkip={question.required ? undefined : () => handleSkip(question.id)}
    />
  );
}

Offline Support

The client automatically caches responses when offline:

const client = createSurveyClient({
  baseURL: 'https://api.bytelyst.io/v1',
  productId: 'lysnrai',
  getAuthToken: () => getToken(),
  enableOfflineCache: true  // Enabled by default
});

// Responses are queued when offline
// Flush queue manually or on reconnect
window.addEventListener('online', () => {
  client.flushOfflineQueue();
});

Types

interface ActiveSurvey {
  id: string;
  title: string;
  description?: string;
  questions: Question[];
  currentQuestionIndex: number;
  incentive?: {
    type: 'pro_days' | 'credits';
    amount: number;
  };
}

interface Question {
  id: string;
  type: 'single_choice' | 'multiple_choice' | 'rating' | 'scale' | 
        'nps' | 'text_short' | 'text_long' | 'ranking' | 'dropdown';
  text: string;
  description?: string;
  required: boolean;
  options?: QuestionOption[];
  minValue?: number;
  maxValue?: number;
  maxLength?: number;
  showIf?: ShowIfCondition;
}

interface QuestionOption {
  id: string;
  text: string;
  emoji?: string;
}

interface SurveyAnswer {
  type: string;
  value: Record<string, unknown>;
}

interface SurveyConfig {
  baseURL: string;
  productId: string;
  getAuthToken: () => Promise<string>;
  enableOfflineCache?: boolean;
}

Question Type Reference

Type Answer Format UI Component
single_choice { value: string } Radio group
multiple_choice { values: string[] } Checkboxes
rating { value: number } Star rating (1-5)
scale { value: number } Numeric slider
nps { value: number } 0-10 buttons
text_short { value: string } Single line input
text_long { value: string } Textarea
ranking { order: string[] } Drag-to-sort
dropdown { value: string } Select dropdown

Conditional Logic

Questions can be shown/hidden based on previous answers:

// Question only shows if q1 answer is NOT 9 or 10
{
  id: 'q2',
  type: 'text_long',
  text: 'What could we improve?',
  showIf: {
    questionId: 'q1',
    operator: 'not_equals',
    value: ['9', '10']
  }
}

Operators: equals, not_equals, greater_than, less_than, contains

Error Handling

const [data, error] = await client.submitAnswer('srv_123', 'q1', answer);

if (error) {
  switch (error.code) {
    case 'SURVEY_NOT_FOUND':
      console.error('Survey expired or unavailable');
      break;
    case 'ALREADY_COMPLETED':
      console.error('User already completed this survey');
      break;
    case 'VALIDATION_ERROR':
      console.error('Invalid answer format');
      break;
    default:
      console.error('Survey error:', error.message);
  }
}

Browser Support

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

License

MIT © ByteLyst

Details
npm
2026-03-29 07:53:29 +00:00
3
latest
7.5 KiB
Assets (1)
Versions (1) View all
0.1.0 2026-03-29