ReferenceTypeScript / JavaScript
Agent skill
Installable LLM skill for the TypeScript / JavaScript SDK (configure() + Client(user), evaluate, experiment + track, testing).
An installable agent skill for the TypeScript / JavaScript SDK. Fetch it with shipeasy docs skill --sdk typescript (--install writes it to your agent skills dir), or copy it below — the YAML frontmatter installs with it.
---
name: shipeasy-typescript
description: Use Shipeasy (feature flags, configs, kill switches, A/B experiments, i18n) from TypeScript / JavaScript. Covers configure() + Client(user), getFlag/getConfig/getKillswitch/getExperiment, track, testing, OpenFeature, and the see() error reporter — server (@shipeasy/sdk/server) and browser (@shipeasy/sdk/client).
---
# Shipeasy TypeScript SDK
`@shipeasy/sdk` — one package, two entrypoints: `@shipeasy/sdk/server` (Node /
Cloudflare Worker / Deno, **server** key) and `@shipeasy/sdk/client` (browser,
public **client** key). Everything works from vanilla JS.
> The documented surface is exactly **`configure()`** (setup) and the bound
> **`new Client(user)`** (use), plus the package-level helpers below. For deeper
> docs, fetch any page/snippet from the manifest at
> <https://shipeasy-ai.github.io/sdk/manifest.json> (raw page/snippet URLs below).
## Install
```bash
npm install @shipeasy/sdk
```
## Configure once, evaluate per user
```ts
import { configure, Client } from "@shipeasy/sdk/server"; // or "@shipeasy/sdk/client"
configure({
apiKey: process.env.SHIPEASY_SERVER_KEY!, // browser: clientKey: process.env.NEXT_PUBLIC_SHIPEASY_CLIENT_KEY
attributes: (u: MyUser) => ({ user_id: u.id, plan: u.plan }), // optional; omit = identity
// poll: true // long-running server: keep flags fresh with a background poll (no engine.init() needed)
});
const flags = new Client(currentUser); // browser: await flags.ready() before first read
flags.getFlag("new_checkout"); // boolean (2nd arg = default if not-ready/not-found)
flags.getConfig<{ max: number }>("limits", { defaultValue: { max: 50 } });
flags.getKillswitch("payments"); // global on/off (not user-bound)
flags.getFlagDetail("new_checkout"); // { value, reason }
```
`configure()` is first-config-wins and owns the fetch lifecycle (one-shot by
default; `poll: true` for a background refresh). Construct `new Client(user)` once
per user/request — it binds the user, so no method takes a user argument.
Full reference: <https://shipeasy-ai.github.io/sdk/pages/configuration.md> ·
<https://shipeasy-ai.github.io/sdk/pages/flags.md> ·
snippets <https://shipeasy-ai.github.io/sdk/snippets/release/flags.md> ·
<https://shipeasy-ai.github.io/sdk/snippets/release/configs.md> ·
<https://shipeasy-ai.github.io/sdk/snippets/release/killswitches.md>
## Experiments + track (Client-only, end to end)
```ts
const flags = new Client(currentUser); // construct once per callsite
const { inExperiment, group, params } = flags.getExperiment("hero_cta", {
primary_label: "Sign up", // default params (control / not-enrolled)
});
render(params.primary_label);
flags.logExposure("hero_cta"); // record the exposure where you present it
flags.track("purchase", { value: 42 }); // record a conversion for the bound user
```
`ExperimentResult = { inExperiment: boolean; group: string; params: P }`. Full
reference: <https://shipeasy-ai.github.io/sdk/pages/experiments.md> · track snippet
<https://shipeasy-ai.github.io/sdk/snippets/metrics/track.md>
## i18n
Full i18n ships in this SDK. Wire the loader via the SSR bootstrap (no separate
init), then render with `i18n.t`:
```ts
import { i18n } from "@shipeasy/sdk/client";
i18n.t("checkout.cta", "Place order");
i18n.t("cart.count", "{count} items", { count: cart.length });
```
Full reference: <https://shipeasy-ai.github.io/sdk/pages/i18n.md> · snippets
<https://shipeasy-ai.github.io/sdk/snippets/i18n/setup.md> ·
<https://shipeasy-ai.github.io/sdk/snippets/i18n/render.md>
## Error reporting (see)
```ts
import { see } from "@shipeasy/sdk/server"; // or /client
try {
await submitOrder(order);
} catch (e) {
see(e).causes_the("checkout").to("use cached prices").extras({ order_id: order.id });
}
see.Violation("large query").causes_the("results").to("be trimmed").extras({ rows });
see.ControlFlowException(e).because("because it wasn't an encoded Foo"); // expected — reports nothing
```
Fire-and-forget on the next microtask (no `.send()`). Don't catch what you can't
name a consequence for. You may `see()` then re-throw (links as `caused_by`). Full
reference: <https://shipeasy-ai.github.io/sdk/pages/error-reporting.md> · snippet
<https://shipeasy-ai.github.io/sdk/snippets/ops/see.md>
## Testing — no network
```ts
import {
configureForTesting,
configureForOffline,
Client,
overrideFlag,
clearOverrides,
} from "@shipeasy/sdk/server";
// Seed values up front; reads go through the ordinary new Client(user). Replaces
// prior config, so each test can reconfigure freely.
configureForTesting({
flags: { new_checkout: true },
configs: { limits: { max: 50 } },
experiments: { hero_cta: ["treatment", { primary_label: "Buy now" }] },
});
const flags = new Client({ user_id: "u_1" });
flags.getFlag("new_checkout"); // true
overrideFlag("new_checkout", false); // flip on the spot
clearOverrides(); // drop every override (incl. the seed)
// Offline: evaluate the REAL rules from a snapshot or JSON file, no network.
configureForOffline({ path: "./shipeasy-snapshot.json" });
// or: configureForOffline({ snapshot: { flags, experiments }, flags: { new_checkout: true } });
```
Full reference: <https://shipeasy-ai.github.io/sdk/pages/testing.md>
## OpenFeature
```ts
import { OpenFeature } from "@openfeature/server-sdk";
import { ShipeasyProvider } from "@shipeasy/sdk/openfeature-server"; // or /openfeature-web
// Assumes configure({ apiKey }) ran at startup — the no-arg provider resolves it.
await OpenFeature.setProviderAndWait(new ShipeasyProvider());
await OpenFeature.getClient().getBooleanValue("new_checkout", false, { targetingKey: "u1" });
```
Full reference: <https://shipeasy-ai.github.io/sdk/pages/openfeature.md>
## Advanced
`privateAttributes`, `bucketBy` (custom bucketing unit), `stickyBucketing`
(browser: on by default, `__se_sticky` cookie), manual exposure
(`getExperiment(..., { logExposure: false })` + `flags.logExposure(name)`),
`onChange(cb)` (requires `configure({ poll: true })`) / browser `subscribe()`.
Devtools overlay: `Shift+Alt+S` or `?se=1`. Full reference:
<https://shipeasy-ai.github.io/sdk/pages/advanced.md>Was this page helpful?✎ Edit this page