@farthershore/business
v3.2.0
Published
Farther Shore Business-as-Code SDK — declare your software business in TypeScript
Maintainers
Readme
@farthershore/business
The functional Business-as-Code SDK for declaring routes, meters, plans, resources, and backends in ordinary TypeScript. Farther Shore compiles these declarations into deterministic Manifest IR and applies edge authorization, limits, usage metering, and billing.
Status:
3.2.0. This SDK versions independently from the frontend and backend SDKs.
Install
pnpm add @farthershore/businessThe 3.x API is a functional value model. Use one namespace import:
fs.business() returns an opaque handle whose compiled manifest is held in a
non-exported package singleton. Raw objects and schema-valid JSON cannot
impersonate an SDK-produced business. A business built by a separately bundled
physical copy of the SDK is therefore rejected; run that copy in its own process
or resolve every business module to the same installed package instance.
import * as fs from "@farthershore/business";
const requests = fs.requests();
const tokens = fs.meter("tokens", { unit: "token" });
const seats = fs.resource("seats", { cap: fs.scope.subscription });
const chat = fs.route("/v1/chat", {
post: { costs: [requests.fixed(1)], reports: [tokens] },
});
const admin = fs.route("/v1/admin", {
post: { surfaces: [fs.surfaces.api] },
});
fs.plan("pro", {
kind: fs.plan.kind.flat,
price: fs.money.usd(49).monthly(),
grants: [chat, admin],
limits: [tokens.perMonth(1000000), seats.max(25)],
});
export default fs.business();Authoring model
Declaration functions return immutable, branded refs. Cross-references accept
those refs, so a typo cannot silently name a missing meter, backend, resource,
route, or grant group. Platform-controlled vocabulary is grouped under typed
namespaces such as fs.surfaces, fs.scope, and fs.money; builder-owned
labels such as a meter's unit remain strings.
Prices use human major units: fs.money.usd(49).monthly() means USD 49 per
month and lowers to exact integer minor units. Every plan declares its kind
(fs.plan.kind.free | flat | usage | prepaid | hybrid | trial | custom); the
kind is validated against the plan's economics controls (price,
usagePricing, funding, lifecycle, spendPolicy) and is never inferred
from shape. Usage money lives only in fs.pricing() catalogs and funding
buckets — a plan carries no per-meter rate.
Meter limit helpers make the window explicit, for example
tokens.perMinute(600) or tokens.perMonth(1000000).
Plans grant route refs directly. fs.group() may bundle route refs for reuse.
Features, capabilities, feature gates, and string-key authorization are not
part of the 3.x authoring surface. RBAC enablement is platform-managed — a
dashboard/API flag on the Business, not a repo-authored declaration; frontend
permission strings remain the independent component/member authorization
vocabulary.
Subscriber cohorts are also platform-owned release state. Builders author plan refs and route groups; Core pins subscribers to immutable compiled-plan cohorts during apply and migration. Do not put cohort identifiers or RBAC flags into a Business SDK program or handcrafted Manifest IR.
Folder discovery and isolation
The conventional program lives under business/; the managed starter uses
business/business.ts, but discovery is filename-agnostic:
business/
business.ts
routes.ts
plans.tsThe compiler imports every source module under business/ in canonical order.
Exactly one module must default-export fs.business(). Declarations register in
a compile-scoped registry that is reset in a fresh worker realm for every
build, so separate builds and duplicate physical SDK installations cannot
cross-contaminate each other.
Ordinary TypeScript is inert: helper functions, unrelated imports, computed
constants, comments, and extra exports do not enter the manifest. Only
SDK-created declarations and the sealed default fs.business() result are
consumed. A module that throws during import produces a file-located build
error.
Determinism
Business modules must be deterministic. The platform compiles the program twice and rejects differing hashes. Avoid time, randomness, network calls, filesystem reads, and environment-dependent declarations.
Main declarations
fs.requests()andfs.meter()declare metering dimensions.fs.route(path, operations)declares path-first HTTP operations.fs.plan(key, options)declares price, direct route grants, limits, credits, trials, spend bounds, and archival behavior.fs.resource()declares counted resources;resource.max(n)creates a typed per-plan ownership limit.fs.backend()declares a backend and returns a route-bindable ref.fs.group()bundles route refs for reuse across plans.fs.business()seals the registry and is the sole valid default export.
Frontend pages and navigation live in the editable frontend/ application.
Route surfaces declare whether an operation is callable from API credentials,
the platform UI, or both; they do not declare frontend routes.
Routes use concrete or OpenAPI-style {parameter} paths and list the HTTP
operations that actually exist. Wildcards are non-granting selectors for
attaching shared metering to separately declared routes:
const tokens = fs.meter("tokens", { unit: "token" });
const createChat = fs.route("/v1/chat", {
post: { surfaces: [fs.surfaces.api] },
});
const getChat = fs.route("/v1/chat/{id}", {
get: { surfaces: [fs.surfaces.api, fs.surfaces.ui] },
});
fs.meterRoutes("chat-tokens", "/v1/chat/**", {
reports: [tokens],
});
fs.plan("pro", {
kind: fs.plan.kind.flat,
price: fs.money.usd(49).monthly(),
grants: [createChat, getChat],
limits: [tokens.perMonth(1000000)],
});meterRoutes(key, target, options) always carries an author-supplied stable
key. A wildcard string, grant group, or array target is a structural metering
overlay on the matched routes; a single route ref target is that route's
commerce metering binding (reports of meters declared with measures, plus
admission bounds). It never creates or grants an operation, and a selector that
matches no declared route fails the build. A route-local reports or costs
binding replaces inherited wildcard meters for that operation;
An operation with no costs/reports (and no overlay match) is unmetered.
Business identity and presentation metadata are platform-owned and are not authored in this SDK.
