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

@nativesquare/refurbed

v0.1.0

Published

A refurbed component for Convex.

Readme

@nativesquare/refurbed

A Convex component that syncs your Refurbed merchant data into local Convex tables — giving you reactive queries, multi-tenant isolation, and zero Refurbed API code in your app. Orders, offers, market offers, and reference data are all available through a simple class-based client or direct component function calls.

Installation

npm install @nativesquare/refurbed

Quick Start

1. Register the component

Create (or update) your convex/convex.config.ts:

// convex/convex.config.ts
import { defineApp } from "convex/server";
import refurbed from "@nativesquare/refurbed/convex.config.js";

const app = defineApp();
app.use(refurbed);

export default app;

2. Instantiate the client

In any Convex function file, create a Refurbed instance:

import { Refurbed } from "@nativesquare/refurbed";
import { components } from "./_generated/api";

const refurbed = new Refurbed(components.refurbed);

3. Create a connection and start syncing

import { Refurbed } from "@nativesquare/refurbed";
import { components } from "./_generated/api";
import { mutation } from "./_generated/server";
import { v } from "convex/values";

const refurbed = new Refurbed(components.refurbed);

export const setupRefurbed = mutation({
  args: { organizationId: v.string(), apiKey: v.string() },
  returns: v.string(),
  handler: async (ctx, args) => {
    // Creates the connection and enables sync (all entity types on by default)
    return await refurbed.createConnection(ctx, {
      organizationId: args.organizationId,
      apiKey: args.apiKey,
    });
  },
});

Three independent cron jobs run every 5 minutes, syncing orders (with order items), offers, and market offers automatically. Your data stays at most 5 minutes stale under normal operation.

Connection Management

Connections link a Refurbed merchant account to your Convex app. Each connection is scoped by organizationId for multi-tenant isolation.

Create a connection

const connectionId = await refurbed.createConnection(ctx, {
  organizationId: "org-acme",
  apiKey: "rfbd_live_...",
});

Throws if a connection already exists for the given organizationId. All sync toggles are enabled by default.

List connections

import { query } from "./_generated/server";

export const listConnections = query({
  args: {},
  returns: v.any(),
  handler: async (ctx) => {
    return await refurbed.listConnections(ctx);
  },
});

Returns all connections with organizationId, sync toggles, last-sync timestamps, sync cursors, and error info. API keys are never returned.

Update a connection (credential rotation)

await refurbed.updateConnection(ctx, {
  connectionId: "...",
  apiKey: "rfbd_live_new_...",
});

Replaces the stored API key and clears any syncError state, re-enabling sync on the next cron cycle.

Delete a connection

await refurbed.deleteConnection(ctx, {
  connectionId: "...",
});

Removes the connection and cascade-deletes all associated synced data (orders, order items, offers, market offers) for that organization. No other organization's data is affected.

Querying Synced Data

Synced data queries read from local Convex tables — they are reactive Convex queries, so subscribed clients auto-update when data changes. No API calls are made.

Orders

import { query } from "./_generated/server";
import { v } from "convex/values";

export const listOrders = query({
  args: { organizationId: v.string() },
  returns: v.any(),
  handler: async (ctx, args) => {
    return await refurbed.listOrders(ctx, {
      organizationId: args.organizationId,
    });
  },
});

Filter by status and limit results:

const shipped = await refurbed.listOrders(ctx, {
  organizationId: "org-acme",
  status: "shipped",
  limit: 10,
});

Get a single order:

const order = await refurbed.getOrder(ctx, {
  organizationId: "org-acme",
  refurbedId: "12345",
});

Order Items

const items = await refurbed.listOrderItems(ctx, {
  organizationId: "org-acme",
  orderId: "12345",
  limit: 50,
});

Get a single order item:

const item = await refurbed.getOrderItem(ctx, {
  organizationId: "org-acme",
  refurbedId: "67890",
});

Offers

const offers = await refurbed.listOffers(ctx, {
  organizationId: "org-acme",
  status: "ACTIVE",
  limit: 20,
});

const offer = await refurbed.getOffer(ctx, {
  organizationId: "org-acme",
  refurbedId: "42",
});

Market Offers

const marketOffers = await refurbed.listMarketOffers(ctx, {
  organizationId: "org-acme",
  offerId: "42",
  limit: 20,
});

const marketOffer = await refurbed.getMarketOffer(ctx, {
  organizationId: "org-acme",
  refurbedId: "42:de", // compound key: "offerId:marketCode"
});

Pass-Through Reference Data

These methods call the live Refurbed API in real time (no local caching). They require an ActionCtx — use them inside Convex action handlers.

Markets & Currencies

import { action } from "./_generated/server";
import { v } from "convex/values";

export const getMarkets = action({
  args: { organizationId: v.string() },
  returns: v.any(),
  handler: async (ctx, args) => {
    return await refurbed.listMarkets(ctx, {
      organizationId: args.organizationId,
    });
  },
});
const market = await refurbed.getMarket(ctx, {
  organizationId: "org-acme",
  marketId: "de",
});

const currencies = await refurbed.listCurrencies(ctx, {
  organizationId: "org-acme",
});

const currency = await refurbed.getCurrency(ctx, {
  organizationId: "org-acme",
  currencyId: "EUR",
});

Instances, Shipping Profiles & Catalog Templates

const instance = await refurbed.getInstance(ctx, {
  organizationId: "org-acme",
  instanceId: "inst-1",
});

const profiles = await refurbed.listShippingProfiles(ctx, {
  organizationId: "org-acme",
});

const templates = await refurbed.getCatalogTemplates(ctx, {
  organizationId: "org-acme",
});

Sync Control

Control which entity types are synced without redeployment.

Update individual toggles

Updates the specified toggles on all connections — omitted toggles remain unchanged.

// Disable order sync on all connections, leave offers and market offers unchanged:
await refurbed.updateSettings(ctx, { syncOrdersEnabled: false });

// Enable all three toggles explicitly:
await refurbed.updateSettings(ctx, {
  syncOrdersEnabled: true,
  syncOffersEnabled: true,
  syncMarketOffersEnabled: true,
});

Bulk enable / disable

await refurbed.enableAllSync(ctx);   // Enable all sync toggles on all connections
await refurbed.disableAllSync(ctx);  // Disable all sync toggles on all connections

Manual sync trigger

import { action } from "./_generated/server";
import { v } from "convex/values";

export const triggerSync = action({
  args: { connectionId: v.string() },
  returns: v.null(),
  handler: async (ctx, args) => {
    return await refurbed.triggerSync(ctx, {
      connectionId: args.connectionId,
    });
  },
});

Triggers an immediate sync for all enabled entity types on the given connection. Requires ActionCtx because it makes HTTP calls to the Refurbed API.

API Reference

| Method | Context | Data Source | Description | |--------|---------|-------------|-------------| | createConnection(ctx, { organizationId, apiKey }) | Mutation | Local write | Create a new merchant connection | | listConnections(ctx) | Query | Local read | List all connections (no API keys) | | updateConnection(ctx, { connectionId, apiKey }) | Mutation | Local write | Rotate API key, clear sync errors | | deleteConnection(ctx, { connectionId }) | Mutation | Local write | Delete connection + cascade data | | listOrders(ctx, { organizationId, status?, limit? }) | Query | Synced local | List synced orders with filters | | getOrder(ctx, { organizationId, refurbedId }) | Query | Synced local | Get single order by Refurbed ID | | listOrderItems(ctx, { organizationId, orderId, limit? }) | Query | Synced local | List order items for an order | | getOrderItem(ctx, { organizationId, refurbedId }) | Query | Synced local | Get single order item | | listOffers(ctx, { organizationId, status?, limit? }) | Query | Synced local | List synced offers with filters | | getOffer(ctx, { organizationId, refurbedId }) | Query | Synced local | Get single offer | | listMarketOffers(ctx, { organizationId, offerId?, limit? }) | Query | Synced local | List synced market offers | | getMarketOffer(ctx, { organizationId, refurbedId }) | Query | Synced local | Get single market offer | | triggerSync(ctx, { connectionId }) | Action | Live API | Manually trigger data sync | | updateSettings(ctx, { syncOrdersEnabled?, syncOffersEnabled?, syncMarketOffersEnabled? }) | Mutation | Local write | Update sync toggles | | enableAllSync(ctx) | Mutation | Local write | Enable all sync toggles | | disableAllSync(ctx) | Mutation | Local write | Disable all sync toggles | | listMarkets(ctx, { organizationId }) | Action | Live API | List all markets | | getMarket(ctx, { organizationId, marketId }) | Action | Live API | Get market by code | | listCurrencies(ctx, { organizationId }) | Action | Live API | List all currencies | | getCurrency(ctx, { organizationId, currencyId }) | Action | Live API | Get currency by code | | getInstance(ctx, { organizationId, instanceId }) | Action | Live API | Get instance by ID | | listShippingProfiles(ctx, { organizationId }) | Action | Live API | List shipping profiles | | getCatalogTemplates(ctx, { organizationId }) | Action | Live API | Get catalog templates |

Configuration Reference

Sync toggles are per-connection boolean flags that control which entity types are synced by the cron jobs:

| Toggle | Default | Cron Interval | Entities Synced | |--------|---------|---------------|-----------------| | syncOrdersEnabled | true | 5 minutes | Orders + Order Items (coupled) | | syncOffersEnabled | true | 5 minutes | Offers | | syncMarketOffersEnabled | true | 5 minutes | Market Offers |

  • Toggles can be changed at runtime via updateSettings, enableAllSync, or disableAllSync — no redeployment needed.
  • Each entity type syncs independently — a failure in one does not prevent the others from completing.
  • On auth failure (401/403), the connection is flagged with syncError and sync stops for that entity. Use updateConnection with a new API key to clear the error and resume.

Direct Component Access

For advanced use cases, you can call component functions directly instead of using the Refurbed class:

import { query, mutation, action } from "./_generated/server";
import { components } from "./_generated/api";
import { v } from "convex/values";

// Query — use ctx.runQuery
export const listOrders = query({
  args: { organizationId: v.string() },
  returns: v.any(),
  handler: async (ctx, args) => {
    return await ctx.runQuery(
      components.refurbed.public.listOrders,
      args,
    );
  },
});

// Mutation — use ctx.runMutation
export const createConnection = mutation({
  args: { organizationId: v.string(), apiKey: v.string() },
  returns: v.string(),
  handler: async (ctx, args) => {
    return await ctx.runMutation(
      components.refurbed.public.createConnection,
      args,
    );
  },
});

// Action — use ctx.runAction
export const triggerSync = action({
  args: { connectionId: v.string() },
  returns: v.null(),
  handler: async (ctx, args) => {
    return await ctx.runAction(
      components.refurbed.public.triggerSync,
      args,
    );
  },
});

All public functions are available under components.refurbed.public.*. Use ctx.runQuery for queries, ctx.runMutation for mutations, and ctx.runAction for actions.

License

Apache-2.0