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

@cohiva/support-widget

v1.4.2

Published

Reusable Support Ticket widget with diagnostics capture and typed payload generation

Readme

Cohiva Support Widget

Reusable React support and feedback widget for Cohiva apps. The package owns the widget UI, validation, diagnostics capture, typed feedback payload generation, browser submission adapters, and shared server helpers for thin POST /api/feedback/github-issue host routes.

Features

  • Floating bottom-right Support Ticket launcher.
  • Two-step flow for bug, feature, feedback, and improvement submissions.
  • Built-in validation, structured description generation, and typed SubmitFeedbackRequest payloads.
  • Diagnostics capture for route visits, user interactions, browser metadata, and runtime warnings/errors.
  • Diagnostics redaction hooks and configurable clear policies.
  • Default browser submission to POST /api/feedback.
  • Browser-side GitHub issue proxy submission to POST /api/feedback/github-issue.
  • Shared server helpers that turn the existing widget payload into a GitHub issue without moving secrets into the browser.
  • Optional Open Issues/Resolved viewer with GitHub issue comments, visible customer updates, closed-ticket reopen, and open-ticket close-with-reason.
  • Light, dark, and system theme modes with label and class-name override surface.
  • CI-gated release process with 100% source coverage thresholds and Playwright example-host e2e coverage.

Submission Modes

When onSubmit is not provided, the widget supports two built-in submission paths:

  • central_api (default): POSTs to ${apiBaseUrl}/api/feedback
  • github_issue_proxy: POSTs to ${apiBaseUrl}/api/feedback/github-issue

Use a custom onSubmit when the host app needs custom transport, auth behavior, retries, or a different backend route.

Install

npm install @cohiva/support-widget

Also include package CSS once in your app:

import '@cohiva/support-widget/styles.css';

Quick Start

import { SupportWidget } from '@cohiva/support-widget';

export function App() {
  return (
    <SupportWidget
      config={{
        appName: 'Cohiva Admin',
        environment: 'production',
        repoSlug: 'cohiva/cohiva-admin',
        apiBaseUrl: 'https://api.cohiva.app',
        getAuthToken: () => localStorage.getItem('token'),
        getCurrentRoute: () => ({
          pathname: window.location.pathname,
          href: window.location.href,
          title: document.title,
        }),
        getCurrentUserContext: () => ({
          user_id: 'u-123',
          business_id: 'b-123',
          role: 'admin',
        }),
      }}
    />
  );
}

Custom Submit Handling

import { SupportWidget, createCentralFeedbackClient } from '@cohiva/support-widget';

const submitFeedback = createCentralFeedbackClient({
  apiBaseUrl: 'https://api.cohiva.app',
  getAuthToken: () => window.sessionStorage.getItem('token'),
});

export function AppSupport() {
  return (
    <SupportWidget
      config={{
        appName: 'Cohiva Admin',
        environment: 'production',
        repoSlug: 'cohiva/cohiva-admin',
        apiBaseUrl: 'https://api.cohiva.app',
        getCurrentRoute: () => ({
          pathname: window.location.pathname,
          href: window.location.href,
          title: document.title,
        }),
        getCurrentUserContext: () => ({
          user_id: 'u-123',
          business_id: 'b-456',
          role: 'owner',
        }),
        diagnostics: {
          captureConsoleWarnings: true,
          clearPolicy: 'on_success',
          redact: (entry) => {
            if (entry.message.includes('password')) {
              return null;
            }
            return {
              ...entry,
              message: entry.message.replace(/token=[^\s]+/g, 'token=[redacted]'),
            };
          },
        },
        theme: {
          mode: 'system',
          launcherLabel: 'Support Ticket',
          classNames: {
            launcher: 'my-app-launcher',
            dialog: 'my-app-dialog',
          },
        },
      }}
      onSubmit={submitFeedback}
      onSuccess={(result) => {
        console.info('Support ticket submitted', result.feedback_id);
      }}
      onError={(error) => {
        console.error('Support ticket failed', error);
      }}
    />
  );
}

Thin GitHub Issue Proxy Route

For submission.mode = 'github_issue_proxy', host apps keep a thin server route and let the shared package own the GitHub issue creation flow:

import { createGitHubIssueProxyHandler } from '@cohiva/support-widget';

export const POST = createGitHubIssueProxyHandler({
  githubAppId: process.env.GITHUB_APP_ID!,
  githubAppPrivateKey: process.env.GITHUB_APP_PRIVATE_KEY!,
  defaultRepoSlug: 'cohiva/cohiva-crunch',
  allowedRepoSlugs: process.env.FEEDBACK_GITHUB_REPOS,
});

allowedRepoSlugs accepts a string[], a comma/newline-delimited string, or a JSON array string. The helper keeps GitHub App secrets on the server and owns repo resolution, installation token exchange, issue title/body rendering, GitHub issue creation, and normalized JSON errors.

Successful GitHub issue proxy responses use this shape:

{
  "message": "GitHub issue created successfully",
  "issue_number": 123,
  "issue_url": "https://github.com/org/repo/issues/123",
  "repo_slug": "org/repo"
}

GitHub Issues Viewer Route

Enable config.issues.enabled to show Open Issues and Resolved tabs. Mount createGitHubIssueListHandler at /api/feedback/github-issues to support list/detail reads, user comments, and ticket closure:

import { createGitHubIssueListHandler } from '@cohiva/support-widget';

export const GET = createGitHubIssueListHandler({
  githubAppId: process.env.GITHUB_APP_ID!,
  githubAppPrivateKey: process.env.GITHUB_APP_PRIVATE_KEY!,
  defaultRepoSlug: 'cohiva/cohiva-crunch',
  allowedRepoSlugs: process.env.FEEDBACK_GITHUB_REPOS,
});

export const POST = GET;

POST /api/feedback/github-issues/:issue_number/comments adds a GitHub issue comment. If the issue is closed, the shared handler reopens it after adding the comment. The widget also shows the submitted comment below the ticket description after GitHub accepts it.

POST /api/feedback/github-issues/:issue_number/close accepts { "reason": "Close reason" }. The shared handler saves the reason as a GitHub issue comment, then closes the issue. The widget only shows this action on open tickets and displays the close reason below the ticket description after GitHub accepts it.

Host Responsibilities

The package owns the shared feedback flow and shared GitHub issue creation logic. Each host app still owns:

  • mounting the widget
  • providing app, route, and auth context
  • deciding whether the widget is visible
  • wiring any app-specific auth checks on POST /api/feedback/github-issue
  • wiring any app-specific auth checks on the issues list/detail/comment/close route
  • injecting GitHub App secrets on the server
  • any repo-specific issue title/body customization beyond the shared default

Development

npm install
npm run dev:example
npm run lint
npm run typecheck
npm run test:run
npm run test:coverage
npm run test:e2e
npm run build
npm run test:smoke
npm run check

Test Commands

npm run test:unit
npm run test:component
npm run test:run
npm run test:smoke
npm run test:coverage
npm run test:e2e

npm run test:coverage enforces 100% line, statement, function, and branch coverage for the package source, excluding the public barrel file and type-only export file. npm run test:e2e runs the Playwright example-host flow.

Release Workflow Summary

  1. Bump package.json to the version you want to publish.
  2. Push that commit to master.
  3. GitHub Actions runs the CI gate:
  • npm run lint
  • npm run typecheck
  • npm run test:coverage
  • npm run build
  • npm run test:smoke
  • npm run test:e2e
  1. Only after CI succeeds, publish.yml checks whether that exact version is already on npm and publishes it if it is new.

Local preflight:

npm run check
npm run test:e2e

Docs

  • docs/public-api.md
  • docs/configuration.md
  • docs/diagnostics.md
  • docs/theming.md
  • docs/accessibility.md
  • docs/development.md
  • docs/testing.md
  • docs/releasing.md
  • docs/github-actions.md
  • docs/repo-secrets-and-publishing.md
  • docs/consuming-the-package.md
  • docs/github-actions-setup-summary.md

Node Version Note

CI uses Node 20.x. Local development should use Node 20 for best alignment with CI validation and release pipelines.