@farthershore/farthershore-js
The browser client, React hooks, and managed component kit.
The browser client, React hooks, and managed component kit.
@farthershore/farthershore-js is the Frontend SDK — the browser integration
layer between a static frontend artifact and the platform. Your frontend code
never sees Core URLs, gateway URLs, or auth endpoints; it expresses intent
and the SDK decides where each request goes (Core for platform concerns, the
Gateway for builder features), how it's authenticated, and the host/env scoping.
Pin it in lockstep with @farthershore/product and
@farthershore/backend.
pnpm add @farthershore/farthershore-js # react is an optional peer (for /react)
import { createFartherShoreClient } from "@farthershore/farthershore-js";
const fs = createFartherShoreClient({ coreUrl: "https://core.farthershore.com" });
const { product, plans } = await fs.bootstrap(); // → Core (discover this host)
await fs.auth.signIn({ apiKey: "fsk_test_…" }); // persona/preview env
const usage = await fs.usage.snapshot(); // → Core
fs.setApiKey("fsk_live_…");
const forecast = await fs.feature("weather").json("/forecast?city=NYC"); // → Gateway
On platform-served portals, createFartherShoreClient() needs no config —
the edge injects window.__FS_CONFIG__ and the SDK falls back to it. Everywhere
else coreUrl is required. Constructors:
| Factory | Use |
|---|---|
createFartherShoreClient(config) | The browser client. coreUrl is usually the only required field; portalHost defaults to window.location.host. |
createFartherShoreClientFromEnv() | Reads config from the bundler env / edge-injected window.__FS_CONFIG__. |
createServerClient(config) | SSR alias — pass portalHost, fetch, and a per-request getToken (no browser globals). |
Config fields: coreUrl, portalHost, productId, environmentId,
organizationId, gatewayUrl, apiKey, getToken, fetch.
The client routes each namespace to Core or the Gateway:
| Namespace | Routes to | Methods |
|---|---|---|
fs.bootstrap() | Core (public resolve) | discover product / env / gateway / capabilities (memoized) |
fs.product | (bootstrap) | get() |
fs.auth | Core | getSession(), signIn({ apiKey }), signOut(), setToken() |
fs.keys | Core | list(), create(), revoke(), rotate() |
fs.usage | Core | summary(), events(), snapshot() |
fs.billing | Core | subscription(), openBillingPortal(), creditBalance() |
fs.plans | Core / (bootstrap) | list(), subscribe() |
fs.feature(name) / fs.invoke(path) | Gateway | fetch(path), json(path) |
fs.setApiKey(key) | — | set the consumer key for Gateway calls |
Core calls use the consumer session token (Authorization: Bearer);
Gateway calls use the consumer API key (fsk_…).
/react)Hooks live in a subpath; react is an optional peer. Each hook returns
{ data, loading, error, refresh } plus its mutations.
import { createFartherShoreClient } from "@farthershore/farthershore-js";
import { FartherShoreProvider, useApiKeys } from "@farthershore/farthershore-js/react";
const fs = createFartherShoreClient({ coreUrl: import.meta.env.VITE_CORE_URL });
function App() {
return (
<FartherShoreProvider client={fs}>
<Portal />
</FartherShoreProvider>
);
}
function ApiKeys() {
const { data, loading, create, revoke, rotate } = useApiKeys();
// …
}
| Hook | Returns |
|---|---|
useFartherShore() | The raw client. |
useBootstrap(), useProduct(), useDeclaredResources() | Bootstrap + product + declared resources. |
useSession() | Session (+ signIn / signOut). |
useApiKeys() | Keys (+ create / revoke / rotate). |
useUsage(), usePinnedUsageRows() | Usage snapshot + pinned billing rows. |
useBilling() | Subscription (+ openBillingPortal). |
usePlans(), useCreditBalance(), useSpendCap() | Catalog, prepaid balance, spend cap. |
useEntitlements(), useFeatureGate(), useCapability(), useCapabilityUsage() | Entitlement + capability checks. |
useApiRateLimit(), useFeature(name) | Rate-limit snapshot; a Gateway feature handle. |
useMe(), useSubscriptionContexts() | The current subscriber + their subscription contexts. |
useTeam(), useAuditLogs(), usePaginatedAuditLogs() | Team + audit logs. |
useResourcesList(), useResource(), useResourceUsage() | Counted resources. |
useUpgrade(), useResourceCap(), useReconcileAfterCheckout() | Upgrade targets; checkout-return reconciliation. |
useOrganization() | Multi-org reactivity (the provider mounts the layer automatically). |
/components)The managed component kit. Every component works with zero props under
<FartherShoreRoot> (data via the SDK hooks); props are optional overrides, and
every one accepts className. Import the stylesheet once:
import "@farthershore/farthershore-js/components/styles.css".
import { FartherShoreRoot, PlansTable, UsageCard, BillingSummary, ApiKeysPanel }
from "@farthershore/farthershore-js/components";
<FartherShoreRoot>
<PlansTable />
<UsageCard />
<BillingSummary />
<ApiKeysPanel />
</FartherShoreRoot>;
| Group | Components |
|---|---|
| Mount + chrome | FartherShoreRoot, useBoot, FsErrorBoundary, FsBranding, FsThemeToggle, FsFooter, FsSplash |
| Theming | FsThemeProvider, useFsTheme |
| Auth (managed) | FsAuthProvider, useFsAuth, useOptionalFsAuth, FsSignIn, FsSignInButton, FsSignOutButton, FsUserButton, SignedIn, SignedOut, AuthLoading |
| Auth-gated routing | RequireAuth, useAuthGuard, planAuthGuard, resolveSignInDestination |
| Data | PlansTable, ApiKeysPanel, UsageCard, BillingSummary, CreditBalance, FeaturePanel, ResourcesPanel, DocsLegal |
| Gating + prompts | FeatureGate, RequireCapability, UpgradePrompt, TopUpPrompt, SpendCapControl, OrgSwitcher, FsLimitBoundary, useLimitHandler |
| Data-rich | TeamPanel, CancelSubscription, TrialBanner, CapabilityUsageCard, OnboardingView, AutoKeyBanner, PaymentHealthBanner, AuditLogTable, RateLimitDisplay, BillEstimator |
| Manifest nav | FsManifestNav — renders a product-authored frontendManifest.nav after a fork |
| Composite | FartherShoreApp, FsPricing — one-tag portal |
| Docs | ProductDocs, useProductDocs, plus the composable shell parts and markdown renderers |
<FartherShoreRoot> is one wrapper = provider + bootstrap gate + theme-from-branding
Branch on FartherShoreApiError.code against the gateway deny vocabulary. See
response & deny codes.
import {
FartherShoreApiError, LimitExceededError, FartherShoreRateLimitedError,
FS_DENY_CODES, isThrottled, isRetryable,
} from "@farthershore/farthershore-js";
try {
await fs.feature("cron-jobs").json("/v1/cron-jobs");
} catch (err) {
if (err instanceof FartherShoreApiError && err.code === FS_DENY_CODES.rate_limited) {
// back off and retry
}
}
Typed error classes: FartherShoreApiError, FartherShoreNetworkError,
FartherShoreAbortError, FartherShoreNotReadyError, FartherShoreConfigError,
LimitExceededError, FartherShoreRateLimitedError, FeatureNotGrantedError,
FartherShoreLifecycleBlockedError, FartherShoreSubscriptionConflictError,
FartherShoreBillingNotConfiguredError. Guards: isRetryable, isThrottled,
isBillingNotConfigured; parsers: parseLimitDescriptor, parseRetryAfterSeconds,
parseRateLimit.
Pure display helpers (a port of the SSR portal's renderers) so any frontend
renders the catalog identically: classifyPlan, isFreePlan, formatPlanPrice,
formatCents, formatPriceFromMicros, formatDate, summarizeMeterPricing,
entitlementBullets, buildGrantRows, quotaForDimension, estimateBill,
describePlanLimit, subscriptionStatusChip, trialDaysRemaining,
paymentHealth, plus the CSV exporters (usageToCsv, downloadCsv) and
top-up bounds (TOP_UP_MIN_CENTS, validateTopUpAmount).
Pin all three SDKs to the same exact version (currently 0.8.2). Under
SemVer 0.x a minor bump may break — caret ranges (^0.8) are unsafe; use an
exact pin or a patch-only tilde (~0.8.2) until 1.0.0.