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

@agoodway/goodleads-quiz-core

v0.1.0

Published

Core quiz funnel engine for GoodLeads lead capture. Provides a multi-step questionnaire state machine with conditional branching, validation, and DOM rendering — all framework-agnostic.

Readme

@agoodway/goodleads-quiz-core

Core quiz funnel engine for GoodLeads lead capture. Provides a multi-step questionnaire state machine with conditional branching, validation, and DOM rendering — all framework-agnostic.

Installation

npm install @agoodway/goodleads-quiz-core
# or
bun add @agoodway/goodleads-quiz-core

Usage

Creating a Quiz Engine

The quiz engine attaches to a container element and drives a multi-step questionnaire:

import { createQuizEngine } from "@agoodway/goodleads-quiz-core";
import type { Questionnaire } from "@agoodway/goodleads-quiz-core";

const questionnaire: Questionnaire = {
  questions: [
    {
      id: "service_type",
      text: "What service do you need?",
      type: "multiple_choice",
      required: true,
      options: [
        { value: "roofing", label: "Roofing" },
        { value: "plumbing", label: "Plumbing" },
        { value: "electrical", label: "Electrical" },
      ],
    },
    {
      id: "zip_code",
      text: "What's your ZIP code?",
      type: "zip",
      required: true,
    },
    {
      id: "email",
      text: "What's your email address?",
      type: "email",
      required: true,
    },
  ],
};

const container = document.getElementById("quiz")!;

createQuizEngine(container, questionnaire, {
  onStepComplete: (stepIndex, questionId, answer, allAnswers) => {
    console.log(`Step ${stepIndex} answered: ${questionId} = ${answer}`);
  },
  onFunnelComplete: (allAnswers) => {
    console.log("Quiz complete!", allAnswers);
    // Submit to your API
  },
});

Conditional Questions

Questions can be shown/hidden based on previous answers:

{
  id: "roof_material",
  text: "What material is your roof?",
  type: "multiple_choice",
  options: [
    { value: "shingle", label: "Shingle" },
    { value: "metal", label: "Metal" },
    { value: "tile", label: "Tile" },
  ],
  conditions: [
    { questionId: "service_type", operator: "equals", value: "roofing" }
  ]
}

Question Types

| Type | Description | Input | |------|-------------|-------| | text | Free-form text | Single-line input | | number | Numeric value | Numeric input | | email | Email address | Email input with validation | | tel | Phone number | Tel input | | zip | 5-digit ZIP code | Numeric input with validation | | textarea | Multi-line text | Textarea | | select | Dropdown select | Select element | | multiple_choice | Option cards | Clickable cards (auto-advances) | | boolean | Yes/No | Two option cards | | address | Full US address | Street, city, state, ZIP composite | | consent | Consent checkbox | Checkbox with legal text |

Condition Operators

| Operator | Description | |----------|-------------| | equals | Exact match (supports arrays for OR logic) | | not_equals | Not equal (supports arrays for AND-NOT logic) | | greater_than | Numeric comparison | | less_than | Numeric comparison | | contains | Case-insensitive substring match |

Hidden Questions with Default Values

Questions can be hidden and pre-populated (useful for tracking/metadata):

{
  id: "source",
  text: "Source",
  type: "text",
  hidden: true,
  default_value: "organic"
}

Resuming a Session

Pass initial state to resume a partially completed quiz:

createQuizEngine(container, questionnaire, callbacks, {
  answers: { service_type: "roofing", zip_code: "90210" },
  qualifyQuestionId: null,
});

Engine Controls

The onEngineReady callback provides controls for programmatic interaction:

createQuizEngine(container, questionnaire, {
  onEngineReady: (controls) => {
    // Prevent user from navigating back past this point
    controls.lockHistory();
  },
});

Utility Functions

import { escapeHtml, getVisibleQuestions } from "@agoodway/goodleads-quiz-core";

// XSS-safe HTML escaping
escapeHtml("<script>alert('xss')</script>");
// → "&lt;script&gt;alert(&#039;xss&#039;)&lt;/script&gt;"

// Get questions visible given current answers
const visible = getVisibleQuestions(questionnaire.questions, currentAnswers);

Container HTML Structure

The engine expects this DOM structure in your container:

<div data-quiz-funnel>
  <div data-quiz-progress>
    <div data-quiz-progress-bar role="progressbar"></div>
    <span data-quiz-progress-text></span>
  </div>
  <div data-quiz-step></div>
  <div data-quiz-nav>
    <button data-quiz-back>← Back</button>
    <button data-quiz-next>Next →</button>
  </div>
</div>

CSS

The engine adds class names like quiz-option-card, quiz-text-input, quiz-label, etc. You provide the styles. See the @agoodway/goodleads-astro package for a complete stylesheet.

API Reference

Types

  • Questionnaire — Top-level quiz definition with questions array and optional messages
  • Question — Individual question with type, options, conditions, and actions
  • QuestionCondition — Conditional visibility rule
  • QuizEngineControls — Programmatic engine control (lockHistory)
  • QuizInitialState — Saved state for session resumption
  • QuestionnaireMessages — Configurable UI messages for completion, disqualification, etc.

Functions

  • createQuizEngine(container, questionnaire, callbacks?, initialState?) — Initialize the quiz engine
  • getVisibleQuestions(questions, answers) — Compute visible questions given current answers
  • collectHiddenDefaultAnswers(questions) — Extract hidden question defaults
  • escapeHtml(str) — XSS-safe HTML escaping
  • renderQuestion(question, currentValue?) — Render a question to HTML string

License

MIT