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

@echoessh/sdk

v1.0.1

Published

Official SDK for Echoes - Centralized feedback platform for developers

Readme

@echoessh/sdk

Official SDK for Echoes - Centralized feedback platform for developers managing multiple SaaS products.

Installation

# Using bun
bun add @echoessh/sdk

# Using npm
npm install @echoessh/sdk

# Using yarn
yarn add @echoessh/sdk

# Using pnpm
pnpm add @echoessh/sdk

Quick Start

Basic Usage

import { Echoes } from "@echoessh/sdk";

const echoes = new Echoes({
  apiKey: "ek_live_xxxxxxxxxxxxx",
});

// Send feedback
await echoes.send({
  category: "bug",
  message: "Button doesn't work on mobile",
  userIdentifier: "[email protected]",
  metadata: {
    browser: "Chrome",
    version: "120.0",
  },
});

// Or use convenience methods
await echoes.bug("Button doesn't work on mobile");
await echoes.feature("Add dark mode support");
await echoes.question("How do I reset my password?");
await echoes.praise("Love the new design!");

React Integration

import { EchoesProvider, FeedbackWidget } from "@echoessh/sdk/react";

function App() {
  return (
    <EchoesProvider config={{ apiKey: "ek_live_xxxxxxxxxxxxx" }}>
      <YourApp />
      <FeedbackWidget
        userIdentifier="[email protected]"
        onSuccess={(id) => console.log("Feedback submitted:", id)}
      />
    </EchoesProvider>
  );
}

API Reference

Echoes Class

Constructor

new Echoes(config: EchoesConfig)

| Option | Type | Required | Default | Description | |--------|------|----------|---------|-------------| | apiKey | string | Yes | - | Your Echoes API key | | baseUrl | string | No | https://echoes.sh | API base URL | | defaultUserIdentifier | string | No | - | Default user identifier | | defaultMetadata | object | No | - | Default metadata for all feedback | | timeout | number | No | 10000 | Request timeout in ms | | debug | boolean | No | false | Enable debug logging |

Methods

send(params: SendFeedbackParams): Promise<FeedbackResponse>

Send feedback with full control over all parameters.

await echoes.send({
  category: "bug", // "bug" | "feature" | "question" | "praise"
  message: "Something went wrong",
  userIdentifier: "[email protected]", // optional
  metadata: { page: "/dashboard" }, // optional
});
bug(message, options?): Promise<FeedbackResponse>

Convenience method for sending bug reports.

await echoes.bug("Login button not working", {
  userIdentifier: "[email protected]",
});
feature(message, options?): Promise<FeedbackResponse>

Convenience method for sending feature requests.

question(message, options?): Promise<FeedbackResponse>

Convenience method for sending questions.

praise(message, options?): Promise<FeedbackResponse>

Convenience method for sending praise/positive feedback.

withUser(userIdentifier): Echoes

Create a new client instance with a default user identifier.

const userEchoes = echoes.withUser("[email protected]");
await userEchoes.bug("Something broke"); // Will include user identifier
withMetadata(metadata): Echoes

Create a new client instance with additional default metadata.

const pageEchoes = echoes.withMetadata({ page: "/settings" });
await pageEchoes.bug("Settings not saving"); // Will include page metadata

React Components

<EchoesProvider>

Context provider for React applications.

<EchoesProvider config={{ apiKey: "ek_live_xxx" }}>
  {children}
</EchoesProvider>

<FeedbackWidget>

Pre-built feedback form component.

<FeedbackWidget
  userIdentifier="[email protected]"
  metadata={{ page: "/home" }}
  onSuccess={(id) => console.log(id)}
  onError={(err) => console.error(err)}
  placeholder="Tell us what you think..."
  submitText="Send Feedback"
  successText="Thanks! 🙏"
  showCategories={true}
  defaultCategory="bug"
  theme="light" // "light" | "dark"
/>

React Hooks

useEchoes(): Echoes

Access the Echoes client from context.

function MyComponent() {
  const echoes = useEchoes();

  const handleClick = async () => {
    await echoes.bug("Something went wrong");
  };

  return <button onClick={handleClick}>Report Bug</button>;
}

useFeedback()

Hook with loading and error states for sending feedback.

function FeedbackForm() {
  const { send, isLoading, isSuccess, error, reset } = useFeedback();
  const [message, setMessage] = useState("");

  const handleSubmit = async (e) => {
    e.preventDefault();
    await send({ category: "bug", message });
  };

  if (isSuccess) {
    return (
      <div>
        <p>Thanks!</p>
        <button onClick={reset}>Send more</button>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit}>
      <textarea
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
      <button disabled={isLoading}>
        {isLoading ? "Sending..." : "Submit"}
      </button>
      {error && <p>{error.message}</p>}
    </form>
  );
}

Error Handling

The SDK throws EchoesError for various error conditions:

import { EchoesError, ErrorCodes } from "@echoessh/sdk";

try {
  await echoes.send({ category: "bug", message: "Test" });
} catch (error) {
  if (error instanceof EchoesError) {
    switch (error.code) {
      case ErrorCodes.INVALID_API_KEY:
        console.error("Check your API key");
        break;
      case ErrorCodes.RATE_LIMITED:
        console.error("Too many requests, try again later");
        break;
      case ErrorCodes.NETWORK_ERROR:
        console.error("Network error:", error.message);
        break;
    }
  }
}

TypeScript Support

The SDK is written in TypeScript and includes full type definitions:

import type {
  EchoesConfig,
  FeedbackCategory,
  SendFeedbackParams,
  FeedbackResponse,
} from "@echoessh/sdk";

Examples

Next.js App Router

// app/providers.tsx
"use client";

import { EchoesProvider } from "@echoessh/sdk/react";

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <EchoesProvider config={{ apiKey: process.env.NEXT_PUBLIC_ECHOES_API_KEY! }}>
      {children}
    </EchoesProvider>
  );
}

Server-Side (Node.js)

import { Echoes } from "@echoessh/sdk";

const echoes = new Echoes({
  apiKey: process.env.ECHOES_API_KEY!,
});

// In your API route or server action
export async function submitFeedback(formData: FormData) {
  await echoes.send({
    category: formData.get("category") as any,
    message: formData.get("message") as string,
    userIdentifier: getCurrentUser()?.email,
  });
}

Custom Feedback Button

import { useEchoes } from "@echoessh/sdk/react";
import { useState } from "react";

function QuickFeedbackButton() {
  const echoes = useEchoes();
  const [isOpen, setIsOpen] = useState(false);

  const handleQuickFeedback = async (type: "👍" | "👎") => {
    await echoes.send({
      category: type === "👍" ? "praise" : "bug",
      message: `Quick feedback: ${type}`,
      metadata: { quick: true },
    });
    setIsOpen(false);
  };

  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>Feedback</button>
      {isOpen && (
        <div>
          <button onClick={() => handleQuickFeedback("👍")}>👍</button>
          <button onClick={() => handleQuickFeedback("👎")}>👎</button>
        </div>
      )}
    </div>
  );
}

License

MIT