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

@refactual/sdk

v0.1.2

Published

Official Refactual REST API client for Node.js and the browser.

Readme

@refactual/sdk

Typed Node.js + browser client for the Refactual platform. Send legacy code in, get modern code back; ship a Shopify store URL in, get a ranked action plan back. All from one client with auto-retry, bearer-token auth, and webhook signature verification.

Preview release (0.x) — the API is stable but minor versions may add fields before 1.0.0. Pin exact versions in production and watch the changelog.

What is Refactual?

Refactual is a developer platform that turns AI agents into a working stack for legacy modernization, document parsing, and ecommerce intelligence. The core surfaces are:

  • Refactor — paste COBOL, Fortran, Perl, VB6, PHP, Java 7, jQuery (40+ source languages); receive idiomatic modern code in your target language with a reviewed diff.
  • Repos — drop a GitHub URL or upload a directory; receive a modernization score, hotspots ranked by tech-debt severity, and Auto-Fix recipes with a dev-weeks effort estimate.
  • Parser — upload PDFs or freeform text; receive structured JSON, field-level diffs, or a generated API schema.
  • Stores — connect Shopify; receive a ranked AI action plan with dollar-impact estimates for revenue, AOV, and CAC moves.

Underneath those products is a single REST API, an MCP server (so Claude, Cursor, and Continue can call the same tools), and 15 Apps Marketplace integrations (GitHub, Slack, Linear, Jira, Sentry, Shopify, etc.) so output flows back into the tools your team already ships with.

This SDK wraps that REST API for TypeScript and JavaScript projects.

Why use this over fetch?

  • Typed responses — ChatCompletion, CreditBalance, UsageDay, Session, Invoice, Model. Inferred return types in your editor; no as any casts.
  • Promise-based, isomorphic — works in Node 18+, Deno, Bun, Cloudflare Workers, and modern browsers. Bring your own fetch for edge runtimes.
  • Auto retry with backoff — 429s and 5xx errors retry up to twice with exponential backoff and Retry-After honoring. You don't write the loop.
  • Retries are billed once — every call carries an idempotency key that its retries reuse, so a timed-out request that is retried can't be charged twice. See Retries are billed once.
  • Bearer + OAuth — long-lived API keys for server code, short-lived client_credentials access tokens for third-party apps and MCP clients.
  • Webhook verification — verifyWebhook(rawBody, signature, secret) with timestamp tolerance. No copy-pasting HMAC code from Stack Overflow.
  • Typed errors — RefactualError exposes status, type (e.g. insufficient_credits, rate_limited), and the raw response.
  • No external runtime deps — uses the platform's native fetch. Tiny install footprint, easy to audit.

Install

npm install @refactual/sdk

pnpm add @refactual/sdk and yarn add @refactual/sdk work too. Requires Node 18+ for native fetch; older Node can pass a fetch polyfill via the fetch option.

Quickstart

Get a key at app.refactual.com/developer. Use a rk_test_* key in CI — it returns deterministic sandbox responses and consumes zero credits.

import Refactual from '@refactual/sdk';

const client = new Refactual({ apiKey: process.env.REFACTUAL_API_KEY! });

// Refactor a snippet of legacy COBOL into TypeScript.
// Inferred type: Promise<ChatCompletion>
const reply = await client.chat.completions.create({
  model: 'claude-sonnet-4-6',
  systemPrompt:
    'You are Refactor. Output only the modernized code in TypeScript. ' +
    'Preserve behavior; no commentary.',
  messages: [
    { role: 'user', content:
      'IDENTIFICATION DIVISION.\n' +
      'PROGRAM-ID. HELLO.\n' +
      'PROCEDURE DIVISION.\n' +
      '    DISPLAY "Hello, world!".\n' +
      '    STOP RUN.\n'
    },
  ],
  maxTokens: 2048,
});

console.log(reply.content);              // modernized TypeScript
console.log(reply.usage.input_tokens);   // 47
console.log(reply.usage.cost_cents);     // 0.42

The response is fully typed — reply.content is string, reply.usage is { input_tokens; output_tokens; cost_cents }. No schema definitions to keep in sync.

What you can build

CI bot that posts modernization scores on PRs. Run client.chat.completions.create on the diff with a system prompt that asks for a hotspot summary, then post the markdown back via octokit.issues.createComment. Block merges if the score regresses.

Slack /refactor command. Wire a Slack slash command to a serverless function that pipes the user's code snippet through Refactual and replies with the modernized version in a code block. Add oauth.token so each workspace authenticates against its own Refactual account.

PDF-to-JSON ingestion pipeline. Run incoming invoices through a chat completion configured with a strict JSON schema in the system prompt; pipe the structured output into Postgres via Prisma. Use the usage and credits endpoints to surface cost-per-document in your admin dashboard.

Storefront action-plan widget. Connect Shopify in the Refactual app, then surface the ranked action plan in your internal merchandising tool by polling client.sessions.list. Webhooks push updates to your dashboard.

Features

  • TypeScript-first; full .d.ts shipped in dist/.
  • Async/await throughout; no callback or event-emitter APIs.
  • Automatic retry on 429 + 5xx with exponential backoff and jitter.
  • One idempotency key per call, minted before the first attempt and reused by every retry — a retried request is billed once.
  • Configurable per-request timeout (default 60s) and retry count.
  • defaultHeaders for correlation IDs, tenant IDs, etc.
  • RefactualError with structured status / type / raw for programmatic recovery.
  • OAuth 2.0 client_credentials grant with scoped tokens.
  • Webhook signature verification (HMAC-SHA256, 5-minute tolerance).
  • Generated OpenAPI types available at @refactual/sdk/generated for advanced cases.

API surface

All methods return Promise<T> with fully typed responses.

| Resource | Description | |---------------------------------|------------------------------------------| | client.chat.completions.create| Submit messages, receive a model reply. | | client.credits.retrieve | Current balance, plan, auto-reload. | | client.usage.list | Daily token + cost rollup. | | client.sessions.list | Recent conversations. | | client.sessions.retrieve | Full transcript for one session. | | client.invoices.list | Free-credit grants and monthly invoices. | | client.models.list | Available models with context windows. | | client.intelligence.create/list/retrieve/upload/delete | Run and poll a Repos modernization report. | | client.autofix.list/retrieve | Turn a Repos hotspot into a PR/diff. | | client.documents.create/list/retrieve/delete | Parser: files → structured JSON. | | client.refactor.code | One-call legacy→modern code translation. | | client.oauth.token | Exchange client_credentials for a token. | | client.request | Low-level escape hatch for new endpoints.| | client.requestRaw | Same, plus status, headers, replayed.| | verifyWebhook (named export) | Validate a webhook signature. |

Full reference: https://app.refactual.com/developer/docs.

Configuration

const client = new Refactual({
  apiKey:         'rk_live_...',
  baseURL:        'https://app.refactual.com', // override for self-hosted
  timeoutMs:      60_000,
  maxRetries:     2,
  defaultHeaders: { 'X-Correlation-Id': myId },
  // For Workers/Deno where global fetch is non-standard:
  // fetch: customFetch,
});

OAuth 2.0 (client_credentials)

Use short-lived access tokens when authenticating third-party apps or MCP clients on behalf of your users:

const tok = await new Refactual({ apiKey: 'rk_placeholder' }).oauth.token({
  clientId:     process.env.REFACTUAL_CLIENT_ID!,
  clientSecret: process.env.REFACTUAL_CLIENT_SECRET!,
  scope:        ['chat:write', 'chat:read'],
});

const client = new Refactual({ accessToken: tok.access_token });
// tok.expires_in is 3600 (1 hour). Re-call oauth.token to rotate.

Webhook verification

import { verifyWebhook } from '@refactual/sdk';
import express from 'express';

const app = express();
app.post('/webhook',
  express.raw({ type: 'application/json' }), // preserve raw body
  (req, res) => {
    try {
      verifyWebhook(
        req.body.toString('utf8'),
        req.headers['refactual-signature'] as string,
        process.env.WEBHOOK_SECRET!,
      );
    } catch (e) {
      return res.status(400).send((e as Error).message);
    }

    const event = JSON.parse(req.body.toString('utf8'));
    switch (event.type) {
      case 'chat.completed':       /* ... */ break;
      case 'credit.low':           /* ... */ break;
      case 'invoice.paid':         /* ... */ break;
      case 'subscription.updated': /* ... */ break;
    }
    res.sendStatus(200);
  });

Error handling

import Refactual, { RefactualError } from '@refactual/sdk';

try {
  await client.chat.completions.create({ messages: [/* … */] });
} catch (e) {
  if (e instanceof RefactualError) {
    // e.status: 402 | 429 | 401 | …
    // e.type:   'insufficient_credits' | 'rate_limited' | 'authentication_error' | …
    // e.raw:    full server payload
    // e.idempotencyKey: the key the call was sent with — resubmit with it for the same single answer
    if (e.type === 'insufficient_credits') topUp();
    else if (e.status === 429) backoffAndRetry();
    else throw e;
  }
}

Retries are billed once

Every non-GET request the SDK sends carries an Idempotency-Key: a UUID minted before the first attempt and reused by every retry of that call — timeouts, 429s, 5xx. The platform runs a keyed chat.completions.create once and remembers its answer for 24 hours, so a retry receives the stored reply instead of starting a second, separately billed completion. reply.replayed is true when that happened.

If a retry lands while the original attempt is still running, the platform answers 409 idempotency_in_flight and the SDK waits for the original to finish — polling with the same key for up to timeoutMs — then returns its result. Only if that budget also runs out does the 409 surface as a RefactualError, with e.idempotencyKey attached so you can resubmit the same call later and still receive the one answer. In-flight polls don't count against maxRetries.

Supply your own key when the same logical call may be issued again by a new process — a queue that can deliver a job twice, a cron that reruns after a crash:

const reply = await client.chat.completions.create(
  { messages: [{ role: 'user', content: code }] },
  { idempotencyKey: `job-${job.id}` },   // any string, up to 200 characters
);
if (reply.replayed) console.log('answered from the earlier attempt — not billed again');

Keys are scoped to your API key. A call that ended in an error is remembered under its key too, so a resubmission returns that same error; start a new call (which mints a new key) to try again. client.requestRaw() returns the same metadata — status, headers, replayed, idempotencyKey — for any endpoint.

Streaming

Streaming token-by-token responses are on the roadmap. Today, chat.completions.create returns the full reply in a single Promise. For long modernization jobs, prefer the sessions endpoints to poll progress, or subscribe to webhook events.

MCP integration

Refactual ships an MCP server that exposes the same surface as this SDK to Claude, Cursor, Continue, and any MCP-aware agent. The OAuth flow above is how third-party MCP clients authenticate against a user's Refactual account. See https://app.refactual.com/developer/docs/mcp for the manifest and tool definitions.

Versioning + support

License

Apache-2.0. See LICENSE.