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

@farthershore/farthershore-js

v0.15.0

Published

Farther Shore Frontend SDK — the browser integration layer between static frontends and the Farther Shore platform (Core + Gateway).

Readme

@farthershore/farthershore-js

The Farther Shore Frontend SDK — the browser + React integration layer for your product's subscriber portal.

Status: 0.15.0. Pre-1.0 (0.x): a minor bump (e.g. 0.9.x0.10.0) may include breaking changes, so a caret range is unsafe — pin this package exactly (0.9.0) or use a patch-only tilde (~0.9.0). Changes are recorded in CHANGELOG.md.

pnpm add @farthershore/farthershore-js   # React is an optional peer (for /react)

What this is

When you build a software business on Farther Shore, your subscribers get a portal where they sign in, pick a plan, manage API keys, watch their usage, and handle billing — and where you can surface your own product features. This SDK is how a frontend talks to that portal.

Your frontend code never deals with Core URLs, Gateway URLs, auth endpoints, or routing rules. It expresses intent — "list my API keys", "subscribe to this plan", "call my weather feature" — and the SDK decides where each request goes (Core for platform concerns like auth, plans, usage, and billing; the Gateway for your product's own features), how it's authenticated, and which host/environment scoping headers to attach.

This guide walks through the five on-ramps for integrating the SDK into a real app: the zero-config happy path, bringing your own auth, bringing your own design system, bringing your own data layer, and writing org-scoped custom hooks.

1. Zero-config happy path

On a portal served by the Farther Shore edge, the client needs no arguments at all. It reads window.__FS_CONFIG__ (injected by the edge) lazily — the first time a request actually needs it — and discovers the businessId from the portal host.

import { createFartherShoreClient } from "@farthershore/farthershore-js";

const fs = createFartherShoreClient();

Wrap your tree in the provider:

import { FartherShoreProvider } from "@farthershore/farthershore-js/react";

function App() {
  return (
    <FartherShoreProvider client={fs}>
      <Portal />
    </FartherShoreProvider>
  );
}

Then read platform state through the hooks. Every read hook returns the same ecosystem shape — { data, error, isLoading, isError, isSuccess, refetch, queryKey } — plus its mutations:

import { useApiKeys } from "@farthershore/farthershore-js/react";

function ApiKeys() {
  const { data, isLoading, isError, error, refetch } = useApiKeys();
  if (isLoading) return <p>Loading…</p>;
  if (isError) return <p>Failed to load keys: {error?.message}</p>;
  return (
    <ul>
      {data?.map((k) => (
        <li key={k.id}>{k.label}</li>
      ))}
    </ul>
  );
}

Want a whole portal with almost no code? <FartherShoreRoot> is one wrapper — provider + bootstrap gate + theme + managed auth — and the managed component kit is self-fed under it:

import {
  FartherShoreRoot,
  PlansTable,
  UsageCard,
  BillingSummary,
  ApiKeysPanel,
} from "@farthershore/farthershore-js/components";
// The SDK is HEADLESS — it ships zero CSS. Provide the component styles
// yourself. A platform-provisioned repo already includes them as plain,
// editable CSS:
import "./styles/components.css";

<FartherShoreRoot client={fs}>
  <PlansTable />
  <UsageCard />
  <BillingSummary />
  <ApiKeysPanel />
</FartherShoreRoot>;

Escape hatch (dev/test only): everywhere there's no injected window.__FS_CONFIG__ — local dev, self-hosting, tests — pass config explicitly:

const fs = createFartherShoreClient({
  coreUrl: "http://localhost:4000", // dev/self-host only; injected on-platform
  portalHost: "croncloud.example.com", // defaults to window.location.host
  businessId: "biz_123", // usually omitted — bootstrap() discovers it
  getToken: () => myAuthProvider.getToken(), // see "BYO auth" below
});

Explicit config always wins over the injected channel. A request attempted with no resolvable coreUrl throws an actionable FartherShoreConfigError.

2. BYO auth

Feed the client your own session token instead of the managed Clerk/persona layer, in one of two ways:

  • At construction, via getToken in the config:

    const fs = createFartherShoreClient({
      getToken: () => myAuthProvider.getToken(), // () => string | null | Promise<...>
    });
  • After construction, via the client method (useful when the token provider isn't ready yet at client-build time):

    fs.setTokenProvider(() => myAuthProvider.getToken());

If you're using <FartherShoreRoot> but want to bring your own auth provider entirely (skip the managed Clerk-satellite/persona layer), pass skipAuth:

<FartherShoreRoot client={fs} skipAuth>
  <MyOwnAuthProvider>
    <Portal />
  </MyOwnAuthProvider>
</FartherShoreRoot>

3. BYO design system

This package is headless: every component renders semantic markup with stable .fs-* class names and ships no CSS at all. You own 100% of the styling — write it against the .fs-* class hooks (and the --fs-* CSS variable tokens the components read for theming). A platform-provisioned repo comes with a full default stylesheet as plain, editable CSS (src/styles/components.css); import it, edit it, or replace it wholesale.

The managed components work standalone under a bare <FartherShoreProvider> — you don't need <FartherShoreRoot> to use them:

import { FartherShoreProvider } from "@farthershore/farthershore-js/react";
import {
  PlansTable,
  ApiKeysPanel,
  UsageCard,
  BillingSummary,
  CreditBalance,
  TeamPanel,
  AuditLogTable,
  RateLimitDisplay,
  BillEstimator,
  CapabilityUsageCard,
  UpgradePrompt,
  TopUpPrompt,
  DocsLegal,
  ResourcesPanel,
  FeaturePanel,
} from "@farthershore/farthershore-js/components";
import "./styles/components.css"; // your own styles for the .fs-* class hooks

<FartherShoreProvider client={fs}>
  <PlansTable />
  <ApiKeysPanel />
</FartherShoreProvider>;

Recompose without forking. PlansTable takes renderRow for a fully custom card, with context for the featured/pending/subscribe state:

<PlansTable
  renderRow={(plan, { featured, pending, onSubscribe, index }) => (
    <MyPlanCard
      key={plan.id}
      plan={plan}
      highlighted={featured}
      busy={pending}
      onClick={onSubscribe}
      rank={index}
    />
  )}
/>

UsageCard takes renderRow per metered dimension:

<UsageCard
  renderRow={(row, index) => (
    <MyUsageRow key={row.key} row={row} index={index} />
  )}
/>

BillingSummary takes named slots (e.g. to replace the actions area):

<BillingSummary slots={{ actions: <MyBillingActions /> }} />

Every component also accepts a plain className on its root for CSS-based restyling.

To bring your own theming under <FartherShoreRoot> instead of the managed theme root, pass skipTheme:

<FartherShoreRoot client={fs} skipTheme>
  <MyThemeProvider>
    <Portal />
  </MyThemeProvider>
</FartherShoreRoot>

4. BYO data layer

Already run TanStack Query or SWR? The /react subpath ships a structural adapter — no runtime dependency on either library, so it costs nothing unless you import it.

TanStack Query:

import { useQuery } from "@tanstack/react-query";
import { fsQueryOptions } from "@farthershore/farthershore-js/react";

const q = fsQueryOptions(fs);
const { data } = useQuery(q.keys.list());
const { data: usage } = useQuery(q.usage.snapshot());

SWR:

import useSWR from "swr";
import { createFsFetcher } from "@farthershore/farthershore-js/react";

const fetcher = createFsFetcher(fs);
const { data } = useSWR("https://api.example.com/v1/x", fetcher);

fsQueryOptions covers the common reads (keys, usage, billing, plans, me, business). For a product-declared resource beyond those, use the generic escape hatch — both return a ready-to-use { queryKey, queryFn } object:

const { data: widgets } = useQuery(q.resource("widgets"));
const { data: widget } = useQuery(q.resourceItem("widgets", widgetId));

5. Org-scope custom hooks

Writing your own hook that reads org-scoped data (a product-declared resource, say)? Use useReadScope() instead of useFartherShore() alone — it composes the client with the reactive active-org id, so folding it into your query key/dep array makes an org switch auto-refetch:

import { useReadScope } from "@farthershore/farthershore-js/react";
import { useAsync } from "@farthershore/farthershore-js/react";

function useMyResource() {
  const { fs, businessId, organizationId } = useReadScope();
  return useAsync(
    (signal) => fs.resources("widgets").list({ signal }),
    [fs, businessId, organizationId],
    ["widgets", businessId, organizationId],
  );
}

fs alone is a stable reference and never reacts to an org switch on its own — businessId/organizationId are what make the dep array and query key scope-correct.

Errors

Reads and mutations throw typed errors you can catch and branch on. The root package keeps the five most-caught classes:

import {
  FartherShoreError,
  FartherShoreApiError,
  LimitExceededError,
  FartherShoreRateLimitedError,
  FartherShoreConfigError,
} from "@farthershore/farthershore-js";

try {
  await fs.keys.list();
} catch (err) {
  if (err instanceof LimitExceededError) showUpgrade(err);
  else if (err instanceof FartherShoreApiError && err.status === 401) {
    // session expired — prompt re-auth
  } else throw err;
}

The full taxonomy — throttle subclasses (ConcurrencyLimitError, AdaptiveThrottleError), deny-envelope parsers, makeApiError, secure-fetch codes, and more — lives at the @farthershore/farthershore-js/errors subpath.

Display helpers

Pure catalog/pricing/usage formatting helpers are grouped under the format namespace, so every frontend renders money, dates, and quotas identically:

import { format } from "@farthershore/farthershore-js";

format.formatCents(1234); // "$12.34"

Testing

@farthershore/farthershore-js/test-utils gives you a fully-typed mock client, a provider wrapper, and typed-error builders — no network, no server:

import {
  createMockFartherShoreClient,
  FartherShoreTestProvider,
  mockLimitExceeded,
} from "@farthershore/farthershore-js/test-utils";

const client = createMockFartherShoreClient({
  keys: { list: async () => [{ id: "key_1", label: "Test key" }] },
});

render(
  <FartherShoreTestProvider client={client}>
    <ApiKeys />
  </FartherShoreTestProvider>,
);

mockLimitExceeded, mockRateLimited, and mockApiError build the typed errors above for asserting your error-handling paths without a real deny from Core or the Gateway.

Learn more

See the platform docs for the full guide to building, publishing, and operating a product on Farther Shore. Breaking changes within 0.x are recorded in CHANGELOG.md — read it before bumping.