@glubean/sdk

Tests, contracts, workflows, and load. One SDK.

Pure TypeScript. Write your auth, config, and network layer once — then reuse it from first request to peak load.

$npm i @glubean/sdk

Four primitives

Separate jobs, never a kitchen sink.

test()

Correctness checks

A single request with typed assertions and a structured trace.

contract()

API promises

An executable agreement that fails the build when behavior drifts.

workflow()

Multi-step scenarios

Named steps that pass state forward, each with its own evidence.

loadScenario()

Performance pressure

Turn a transaction into a repeatable load plan — same auth, same client.

Read like intent

Named steps and typed assertions, not fetch scripts.

tests/products.test.ts
import { test } from "@glubean/sdk";

test("list products", async (ctx) => {
  const base = ctx.vars.require("BASE_URL");

  const data = await ctx.http
    .get(`${base}/products?limit=5`)
    .json();

  ctx.expect(data.products.length).toBe(5);
  ctx.log(`Found ${data.total} products`);
});

What you get

One request, typed assertions, a structured trace.

Configure once

Write auth and config in one file. Reuse it everywhere.

configure() holds your base URL, headers, and secrets. Every test, contract, and load run reads from it — so tests read like intent, not infrastructure.

tests/configure.ts
import { configure } from "@glubean/sdk";

export const { vars, http } = configure({
  vars:    { baseUrl: "BASE_URL" },
  secrets: { apiKey: "API_KEY" },
  http: {
    prefixUrl: "BASE_URL",
    headers: { Authorization: "Bearer {{API_KEY}}" },
  },
});
tests/products.test.ts
import { test } from "@glubean/sdk";
import { http } from "./configure.ts";

test("list products", async ({ expect }) => {
  const data = await http.get("products?limit=5").json();
  expect(data.products.length).toBe(5);
});

// configure() lives in one file, shared across every test.
// Tests read like intent, not infrastructure.

Plugins, not forks

Compose GraphQL, browser, and your own clients.

Add capabilities through the same configure() call, or define your own plugin — all of it keeps the one evidence model.

compose plugins
const { gql, chrome, pay } = configure({
  plugins: {
    gql:    graphql({ endpoint: "{{GQL_URL}}" }),
    chrome: browser({ launch: true }),
    pay:    payments({ baseUrlKey: "PAY_URL" }),
  },
});
definePlugin() — your own
import { definePlugin, configure, test } from "@glubean/sdk";

const payments = (opts: { baseUrlKey: string }) =>
  definePlugin((runtime) => {
    const client = runtime.http.extend({
      prefixUrl: runtime.requireVar(opts.baseUrlKey),
      headers: {
        Authorization: `Bearer ${runtime.requireSecret("STRIPE_KEY")}`,
      },
    });
    return {
      charge: (amount: number) =>
        client.post("charges", { json: { amount } }).json(),
      refund: (id: string) =>
        client.post(`charges/${id}/refund`).json(),
    };
  });

const { pay } = configure({
  plugins: { pay: payments({ baseUrlKey: "PAYMENTS_URL" }) },
});

export const chargeAndRefund = test("charge-refund", async (ctx) => {
  const charge = await pay.charge(2500);
  ctx.expect(charge.status).toBe("succeeded");

  const refund = await pay.refund(charge.id);
  ctx.expect(refund.status).toBe("refunded");
});
@glubean/browser@glubean/graphqldefinePlugin()pg / any client

One evidence model

The same structured evidence — local, CI, and Cloud.

Every run keeps requests, responses, assertions, and traces as structure. Run locally and it stays on your machine; add --upload and the same evidence becomes dashboards, history, and analysis in Cloud.

See what Cloud does with it

Local

glubean run

stays on your machine

CI

ci run --upload

redacted, then sent

Cloud

app.glubean.com

dashboards + history

One SDK. Every kind of API check.

$npm i @glubean/sdk