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

@kyrofeedback/sdk

v0.1.0

Published

Typed server-side client for the KyroFeedback REST API

Readme

@kyrofeedback/sdk

A TypeScript-first, server-side client for the KyroFeedback v1 REST API. It uses the existing API and does not connect to a database or depend on Next.js.

The API currently supports feedback listing, retrieval, and creation; comment listing and creation; and vote state, vote, and unvote. Feedback editing/deletion and comment deletion are not available through v1 and are intentionally not exposed here.

Requirements and installation

  • Node.js 20 or newer (native fetch and AbortController)
  • A KyroFeedback API key for one project

When the package is published, install it with:

npm install @kyrofeedback/sdk

This repository does not publish automatically. Until release, build the package and install its packed tarball from a local path.

cd packages/sdk
npm install
npm test
npm pack
# From the consuming application:
npm install /path/to/kyrofeedback-sdk-0.1.0.tgz

Keep keys on the server

The SDK can use either sk_live_ or pk_live_ keys because both are accepted by these API endpoints. Current keys have the prefix followed by 43 base64url characters. For a server integration, use a secret sk_live_ key and keep it in a server-only environment variable. In Next.js, use KYROFEEDBACK_API_KEY, never NEXT_PUBLIC_KYROFEEDBACK_API_KEY. The client rejects initialization in a browser runtime; do not import it into browser components or send a secret key to a browser. The browser widget continues to use its publishable key and widget.js.

Keys are project-scoped. The API enforces project settings, project isolation, and a current limit of 120 requests per minute per key. Feedback, comment, and vote creation can return 403 when the corresponding anonymous activity setting is disabled. The SDK does not bypass those checks.

Initialize

import { KyroFeedback } from "@kyrofeedback/sdk";

const kyro = new KyroFeedback({
  apiKey: process.env.KYROFEEDBACK_API_KEY!,
});

The default host is https://feedback.kyrocms.com; requests go to its /api/v1 API. For a local or self-hosted instance, set baseUrl to its origin (or an origin with a path prefix):

const kyro = new KyroFeedback({
  apiKey: process.env.KYROFEEDBACK_API_KEY!,
  baseUrl: "http://localhost:3000",
  timeout: 5_000,
});

baseUrl must be an absolute HTTP or HTTPS URL without credentials, query, or fragment. Plain HTTP is accepted only for localhost and loopback development URLs; remote hosts must use HTTPS. A trailing /api/v1 is normalized. The default timeout is 10 seconds. Pass a compatible Fetch API implementation with fetch when needed.

Feedback

List and paginate

The list endpoint returns newest feedback first and supports limit (API range 1–50, default 20), cursor, q, status, and type. The page object preserves meta.nextCursor; request subsequent pages only when needed.

const page = await kyro.feedback.list({
  limit: 20,
  status: "OPEN",
  type: "FEATURE",
  q: "dark mode",
});

for (const item of page.data) {
  console.log(item.title, item.votesCount, item.commentsCount);
}

if (page.meta.nextCursor) {
  const nextPage = await kyro.feedback.list({
    limit: 20,
    cursor: page.meta.nextCursor,
  });
}

Supported status values are OPEN, PLANNED, IN_PROGRESS, COMPLETED, and CLOSED. Supported type values are FEATURE, BUG, QUESTION, and INTEGRATION.

Get and create

const item = await kyro.feedback.get("feedback-id");

const created = await kyro.feedback.create({
  title: "Add dark mode",
  description: "A dark theme would make the dashboard easier to use at night.",
  type: "FEATURE",
  authorName: "Alex", // optional; 100 characters maximum in the API
});

The API requires a title of 3–200 characters and a description of 3–10,000 characters. Feedback creation is anonymous and requires the project’s anonymous feedback setting to be enabled. The response fields are id, title, description, type, status, votesCount, commentsCount, createdAt, and updatedAt.

The v1 API does not support feedback updates or deletion. Those methods are not included.

Comments and replies

const comments = await kyro.comments.list("feedback-id");

const rootComment = await kyro.comments.create("feedback-id", {
  content: "This would help our team too.",
  authorName: "Alex",
});

await kyro.comments.create("feedback-id", {
  content: "Thanks for the detail!",
  parentId: rootComment.id,
});

Comments are returned as a flat, creation-ordered array. Group parentId: null comments as roots and attach replies to their parent ID. The API accepts comment content up to 5,000 characters, requires anonymous comments to be enabled, and validates that a reply’s parent belongs to the same feedback item. Comment deletion is not supported by the v1 API.

Votes and visitor identity

const state = await kyro.votes.get("feedback-id");
const updated = state.voted
  ? await kyro.votes.unvote("feedback-id")
  : await kyro.votes.vote("feedback-id");

The API identifies votes with its kyro_anonymous_id cookie; it does not accept a user ID. The SDK never stores cookies between requests because a long-lived server client may be shared by many visitors. To preserve a particular visitor’s identity in a server route, forward that visitor’s Kyro cookie on each request:

const cookie = request.headers.get("cookie") ?? undefined;
const voteState = await kyro.votes.get("feedback-id", { cookie });
const responseHeaders = new Headers();
const updated = await kyro.votes.vote("feedback-id", {
  cookie,
  onResponse(response) {
    // In a server route, relay Set-Cookie to that same visitor when present.
    const setCookie = response.headers.get("set-cookie");
    if (setCookie) responseHeaders.append("set-cookie", setCookie);
  },
});
return Response.json(updated, { headers: responseHeaders });

RequestOptions also accepts an AbortSignal. Vote state and counts come from the API response; the SDK does not synthesize them.

Request controls

All resource methods accept an optional final RequestOptions argument with signal, cookie, and onResponse. Cookies are opt-in and never persisted. Mutations are not automatically retried.

const controller = new AbortController();
const result = await kyro.feedback.list({ limit: 10 }, { signal: controller.signal });

Errors

All API, transport, and malformed-response failures are KyroFeedbackError instances. status is populated for HTTP responses, and kind categorizes failures as authentication, permission, validation, not_found, rate_limit, server, http, network, timeout, or invalid_response.

The API currently returns errors as { error: { message, status } }; it does not return a stable error code or request ID, so the SDK does not fabricate either.

import { KyroFeedbackError } from "@kyrofeedback/sdk";

try {
  await kyro.feedback.create({ title: "Add dark mode", description: "Please add a dark theme.", type: "FEATURE" });
} catch (error) {
  if (error instanceof KyroFeedbackError) {
    if (error.kind === "permission") console.error("Check anonymous feedback settings.");
    else console.error(error.kind, error.status, error.message);
  }
  throw error;
}

Next.js server-side usage

Keep SDK use in a route handler or server component. A client component should call your route, not import the SDK or receive the secret key.

// app/api/feedback/route.ts
import { KyroFeedback, KyroFeedbackError } from "@kyrofeedback/sdk";

const kyro = new KyroFeedback({ apiKey: process.env.KYROFEEDBACK_API_KEY! });

export async function GET() {
  try {
    const page = await kyro.feedback.list({ limit: 20 });
    return Response.json(page);
  } catch (error) {
    if (error instanceof KyroFeedbackError) {
      return Response.json({ error: error.message }, { status: error.status ?? 502 });
    }
    return Response.json({ error: "Unexpected error" }, { status: 500 });
  }
}

An end-to-end example is in examples/nextjs.

Node.js usage

import { KyroFeedback } from "@kyrofeedback/sdk";

const kyro = new KyroFeedback({ apiKey: process.env.KYROFEEDBACK_API_KEY });
const page = await kyro.feedback.list({ limit: 10, type: "BUG" });
console.log(page.data);

Development, tests, and package verification

From this directory:

npm install
npm run typecheck
npm test
npm run pack:check

npm test compiles the package and runs mocked-fetch tests with Node’s built-in test runner. It does not contact a live service.

Live integration checks

npm run test:integration runs an opt-in suite against a dedicated local or test deployment. It creates feedback and comments and changes a vote; it cannot delete those records. Use two disposable projects with separate keys, enable anonymous feedback, comments, and voting on the first project, and use a third disposable key that you have revoked if you want the revoked-key case. Never use a production project, key, or user data.

Set these server-side variables without printing or committing them:

export KYROFEEDBACK_BASE_URL=http://localhost:3000
export KYROFEEDBACK_TEST_API_KEY=sk_live_REPLACE_WITH_FULL_TEST_PROJECT_ONE_KEY
export KYROFEEDBACK_SECOND_PROJECT_API_KEY=sk_live_REPLACE_WITH_FULL_TEST_PROJECT_TWO_KEY
export KYROFEEDBACK_REVOKED_API_KEY=sk_live_REPLACE_WITH_FULL_REVOKED_TEST_KEY
npm run test:integration

Replace each placeholder with the complete raw disposable key copied from the dashboard; these sample values are intentionally not valid keys.

For a remote dedicated test host, use HTTPS and explicitly set KYROFEEDBACK_ALLOW_REMOTE_TEST_INSTANCE=true after confirming the deployment is isolated. The script refuses feedback.kyrocms.com and refuses remote hosts without that opt-in. It checks authentication, list/create/get, cursor pagination, comments/replies, vote/unvote, a second-project 404, invalid credentials, and (when provided) revoked credentials. The test records are marked with a random ID and remain in the disposable project because v1 has no delete methods.

Build output and declaration files are generated under dist/. To publish after review, update the version, run the checks above, inspect npm pack --dry-run, then publish explicitly from this directory with npm publish --access public. No package has been published by this repository change.